루씬 3.X 버전까지는 IndexReader로부터 Term과 TermFreq를 얻기 위해서

아래와 같은 코드를 사용하였습니다.




IndexReader로부터 

TermFreqVector termFreqVector1 = ir.getTermFreqVector(0, "f"); 와 같은 형태로

document 하나에 대한 TermFreqVector를 얻어내고

이를 통해서 term 배열과 freq 배열을 얻어서 조합하는 방식입니다.


4.0에서는 IndexReader.getTermFreqVector 메서드가 사라지고 대신 아래와 같은 방법을 사용합니다.


IndexReader.getTermVector(0, "f") 메서드를 사용합니다.

이때 Terms라는 클래스의 인스턴스를 얻을 수 있는데, 위 코드의 예에서는 doucment 0번에 대한

인스턴스이므로 하나의 document (위의 예에서는 document 0)가 색인된 역인덱스 파일의 통계 정보를

가지고 있는 것과 같은 값을 가지고 있습니다.


예를 들어서, termEnum.docFreq()는 해당 term을 가진 전체 document의 개수이지만, 위의 경우 전체 document가

하나인것과 같은 상태이므로 1을 return하는 형태입니다.


전체 Terms에 대한 통계정보가 아직 제대로 수집이 되지 않는 부분이 있습니다만 (http://devyongsik.tistory.com/577) 곧 수정이 된다고 합니다.


그리고, Terms도 하나의 document가 아니라

전체 혹은 특정 Term이나 필드에 대한 Terms 인스턴스를 얻어내서 그에 대한

통계를 얻을 수 있는 방법도 있을 것 같습니다.


Posted by 용식
방명록에 남겨진 질문 중 하나에 대한
답을 찾아보려고 이것저것 해보다가 우선 생각나는 방법이 있어서
적어보려고 합니다.

질문의 내용은
루씬의 Explain에서..

0.5987479 = (MATCH) product of:

1.4968698 = (MATCH) sum of:

0.7484349 = (MATCH) weight(label:시크릿 in 0), product of:

0.40649435 = queryWeight(label:시크릿), product of:

2.9459102 = idf(docFreq=1, maxDocs=14)

0.13798599 = queryNorm

1.8411939 = (MATCH) fieldWeight(label:시크릿 in 0), product of:

1.0 = tf(termFreq(label:시크릿)=1)

2.9459102 = idf(docFreq=1, maxDocs=14)

0.625 = fieldNorm(field=label, doc=0)

0.7484349 = (MATCH) weight(label:가든 in 0), product of:

0.40649435 = queryWeight(label:가든), product of:

2.9459102 = idf(docFreq=1, maxDocs=14)

0.13798599 = queryNorm

1.8411939 = (MATCH) fieldWeight(label:가든 in 0), product of:

1.0 = tf(termFreq(label:가든)=1)

2.9459102 = idf(docFreq=1, maxDocs=14)

0.625 = fieldNorm(field=label, doc=0)

0.4 = coord(2/5) 


Match된 Term 정보와 Coord의 정보를 보고 싶다는 것이었는데요
내부 소스를 따라가기가 쉽지 않더라구요..
Explain 정보를 생성해가는 과정에서 지속적으로 정보를 붙여나가도록 되어있기도 하고요..

생각해보니 점수가 아니라 Term 정보와 Coord 값이라면
단순하게 가져 올 수도 있지 않을까 해서 작성해보았습니다.

Explain 메서드를 따라가보면 거의 검색을 한번 더 수행하는 수준의 로직으로 구현이 되어있습니다.

리소스 문제도 있겠지만
따라가기가 정말 어렵더군요 --; 각각의 Explain 항목을 Object로 가져오는 형식이 아니라
검색 수행단계별로 결과 값을 append하여 만드는 형식이라서 중간의 어느 한가지 항목만
가져오는 것이 쉽지 않아 보였습니다.

일단, 질문자께서 원하신 것이 "Match된 키워드와 Coord의 값"이었기 때문에
Analyzer의 TokenStream과 TermVector를 사용하면 꼼수를 부릴 수 있지 않을까 싶어서 만들어보았습니다.

Query에 사용된 질의문을 Analyzer를 사용해 각 Term으로 분리하고 (물론 색인에 사용된 Analyzer여야겠죠)
검색되어 나온 Document의 TermVector를 얻어서 비교하는 방식입니다.

Document는 아래와 같이 2개가 색인 되었다고 가정하겠습니다.

0 : 시크릿 가든 여자배우
1 : 자바 구글 앱 엔진


그리고 이 두개의 문서는 WhitespaceAnalyzer를 사용하여 색인 하였습니다.
또한 색인시 Field 명은 "label"로 옵션으로는 TermVector.YES를 추가하였습니다.




소스는 아래와 같습니다.


IndexReader로부터 검색 된 Document의 ID로 TermFreqVector를 가져옵니다. 이 Object는 해당 Document가
가지고 있는 Term과 각 Term의 개수가 들어있습니다. 물론 색인시 사용된 Analyzer (여기에서는 WhitespaceAnalyzer)에 의해서 추출 된 Term 일 것 입니다.

또한, 질의문을 Analyzer로 다시 분석하여 Term을 얻어냅니다. 그리고 그 Term의 개수가 coord에서의 분모가 되는
maxOverlap의 값이 될 수 있습니다.

이렇게 질의문에서 얻은 Term과 Document에서 얻은 TermFreqVector의 Term을 비교하면
실제 Match된 Term의 개수를 알 수 있는데요 이것이 coord에서 overlap의 값이 됩니다.

위 예제의 경우 결과는 아래와 같습니다.

 [시크릿]

maxOverLap : 1
overLap : 1



만약 질의문을 "시크릿 자바"로 한다면 검색 Document는 2개가 될 것이고
결과는 아래와 같이 나옵니다.

 [시크릿]
maxOverLap : 2
overLap : 1
 
[자바]
maxOverLap : 2
overLap : 1

질의문이 "시크릿 오케이 굿 잡" 이라면
아래와 같이 나올 것 입니다.

[시크릿]
maxOverLap : 4
overLap : 1


제가 현재 알고 있는 지식으로는 이정도가 될 것 같은데..아마 분명히 Explain의 각 값을
쉽게 가져 올 수 있는 그리고 실제 구현된 값대로 가져 올 수 있는 방법이 있을 것 같은데 잘 모르겠네요..ㅋ

질문자분께 도움이 되었으면 좋겠습니다.
Posted by 용식

루씬 인 액션을 보던 중 텀벡터에 대한 얘기가 나와서 살펴보았다.

간단하게 색인을 해 놓고..

소스를 간단하게 만들어서 돌려보았다..

package kr.co.gshs.lucene.test;

import java.io.IOException;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.TermFreqVector;
import org.apache.lucene.index.TermPositionVector;
import org.apache.lucene.index.TermVectorOffsetInfo;

public class TermVectorTest {

public static void main(String[] args) throws IOException {
  IndexReader reader = IndexReader.open("D:/lucene_data/test_index");
  int maxDoc = reader.maxDoc();

  System.out.println("maxDoc = " + maxDoc);

  for(int i = 0; i < maxDoc && i < 10; i++) {
   //System.out.println("i");
   Document doc = reader.document(i);
   String subject = doc.get("TITLE");

   System.out.println("title = " + subject);

   TermFreqVector termFreqVector = reader.getTermFreqVector(i, "TITLE");
   TermPositionVector termPosition = (TermPositionVector)reader.getTermFreqVector(i, "TITLE");

   String[] terms = termFreqVector.getTerms();
   int[] freqs = termFreqVector.getTermFrequencies();

   String[] termsPo = termPosition.getTerms();
   int[] termPo = termPosition.getTermFrequencies();

   //System.out.println("termPo = ["+termPo.length+"]");

   for(int j = 0; j < terms.length; j++) {
    System.out.println("TERM = " + terms[j] + "["+freqs[j]+"]");
   }

   for(int k = 0; k < termPo.length; k++) {
    System.out.println("TERMs = " + termsPo[k] + "["+termPo[k]+"]");
    TermVectorOffsetInfo[] offsets = termPosition.getOffsets(k);
    int[] tp = termPosition.getTermPositions(k);

    for(int kk = 0; kk < tp.length; kk++) {
     System.out.println("term position : " + tp[kk]);
    }

    for(int cp = 0; cp <offsets.length; cp++) {
     System.out.println("start_off_set : " + offsets[cp].getStartOffset());
     System.out.println("end_off_set : " + offsets[cp].getEndOffset());
    }
   }

  }

 }

}

일단 검정 부분만 보면...
IndexReader로 부터 getTermFreqVector 메서드를 통해 TermFreqVector 를 생성하여

getTerms()와 getTermFrequencies()를 사용하여 TITLE 필드에 있는 컨텐츠의

텀과 빈도수를 가져오고 있다.

실제로 색인되어 있는대로 가져오기 때문에 제목이 만약에

"테스트 테스트 테스트"라면 이 넘은

terms[0] = 테스트
freqs[0] = 3
이 된다. 만약 동의어에 의해서 "테스트 입니다" 라는 제목이

"테스트 test 입니다" 라고 색인이 되어 있다고 한다면 (물론 offset이나 position은

테스트와 test가 동일하게 색인)

terms = {테스트, test, 입니다}
freqs = {1, 1, 1} 이 된다.

참고로 이 메서드를 사용하기 위해서는 색인시

Field field = new Field(...., Field.TermVector.YES) 를 주어야 한다.

이제 파랑 부분을 보자.

TermPositionVector termPosition = (TermPositionVector)reader.getTermFreqVector(i, "TITLE");

다운 캐스팅을 통해서 TermPositionVector 를 가져오고 있다.

이 부분을 실행시 만약 색인 할 때
Field.TermVector.WITH_POSITIONS_OFFSETS 이나 WITH_POSITION 조건을 주지 않으면 , ClassCastException이 발생한다.

위 조건을 주어 색인을 하고 TermPositionVector를 가지고 와서

위 for문을 돌리면 아래와 같은 결과가 나온다.

title = Siebel주문Interface항목

TERMs = interface[1]
term position : 2
start_off_set : 8
end_off_set : 17

TERMs = siebel[1]
term position : 0
start_off_set : 0
end_off_set : 6

TERMs = 주문[1]
term position : 1
start_off_set : 6
end_off_set : 8

TERMs = 항목[1]
term position : 3
start_off_set : 17
end_off_set : 19

TermFreqVector 처럼 term와 Freq도 가져올 수 있고
색인되어 있는 정보를 사용하여 텀의 position과 offset을 가져올 수도 있다.

다만..이 정보를 포함해서 색인할때의 속도라던가...그런 것들은 비교를 하지 못 했고..
이걸 어디에 쓰는게 가장 효과적일까...하는 생각이 들기도 한다.;;

여기저기 찾아보니
TermPositionVector는 Highlight 클래스에서 사용하는 것 같다.

색인에 정보가 있으면 TermPositionVector를 가져와서 하이라이트 할 부분을 찾아내고

없으면 Analyzer를 통해 다시 tokenStream을 뽑아내서 찾아내는 방식이 아닐까...

하는 생각이 든다.


그거 말고 어디 또 좋은데 쓸 수 있을것 같은데...;;;

Posted by 용식