검색엔진을 활용하면서 항상 기술적으로 대두되는 이슈가 정렬처리, 실시간검색 단어인듯하다. 다양한 방법으로 Sort처리하고 있지만 루씬메일링리스트에서 찾아본자료에서는 크게 2가지 방법으로 접근하는듯하다. 2가지 방법을 알아보고, 조금 실험적인 방법인 CustomScoreQuery 에 대해서 알아보고자 한다.
- 루씬내부 Sort 클래스를 이용하여 정렬
- 검색한결과를 재정렬하는 방법, 검색범위를 줄여서 정렬하는 방법
루씬내부 Sort 클래스를 이용하여 정렬
SortField sortFeild = new SortField("DATE",SortField.STRING,true); Sort sort = new Sort(sortFeild); TopFieldDocs docs = searcher.search(query,null,200,sort);
위코드와 같이 Sort클래스를 활용하여 루씬내부에서 정렬을 하게끔 처리하도록 한다. 하지만, 루씬내부 Javadoc문서를 살펴보면,
Memory Usage
Sorting uses of caches of term values maintained by the internal HitQueue(s). The cache is static and contains an integer or float array of length IndexReader.maxDoc() for each field name for which a sort is performed. In other words, the size of the cache in bytes is:
4 * IndexReader.maxDoc() * (# of different fields actually used to sort)
내부에서 Sort를 위해 term value값을 캐시하기때문에, 최대검색결과 * 필드형의사이즈 만큼의 메모리를 사용하게 된다. 따라서, 인덱스사이즈가 큰경우 OutOfMemoryException 이 발생하게되고 또한, 요청수가 많게되면 GC로 인한 Hang현상을 경험하게 된다. 즉, 성능상이슈가 발생하게 된다는 것이다.
검색한결과를 재정렬하는 방법, 검색범위를 줄여서 정렬하는 방법
루씬을 사용하여 기본적인 검색을 진행하면 랭킹알고리즘을 적용하여 검색결과를 나타나게 된다. 내부적으로 문서빈도수, 색인어빈도수를 측정하여 랭킹을 계산하게 된다. 아래의 코드는 특정쿼리로 최대 1000개의 문서를 반환하게끔하는 코드이다.
TopScoreDocCollector collector = TopScoreDocCollector.create(1000,false); searcher.search(query,collector); TopDocs docs = collector.topDocs();
이 결과를 바탕으로 다시 내가 원하는 필드로 정렬하는 방식이다. 즉, TopN개에 대한 결과를 미리 처리한 후 정렬하겠다는 것. 요약하면, 검색품질에 대한 어느정도의 필터링을 거친후 정렬하겠다는 방식이다.
첫번째 설명한 루씬내부에서의 정렬은 모든문서가 정렬대상이 되는 반면, 지금 정렬방식은 TopN개만이 검색정렬대상이 된다는 것이다. 특별하게 검색결과에 포함되어야 하는 것은 누락이 될 수 있다는 위험성을 내포한다. TopN개의 수위조절을 어느선에서 정할지에 대한것은 검색결과,성능과의 Trade Off니 운영을하면서 맞추어주는 방법밖에는 없는듯하다. 또한, TopN개에 대한 정렬을 Java단에서 처리하게되면 메모리부족이슈가 발생할 수 있으므로 JNI를 통해서 Native쪽으로 우회하는 방법도 존재하는듯하다. 첫번째,두번째 방법의 경우 모두 정렬에 대한 명쾌한 해답을 제시해주지 못하고 있다.
정말 인덱스사이즈가 클수록 정렬이슈가 커진다. 또한, 검색엔진에서 최신순 정렬은 필수가 아니던가!!! T.T 정렬이슈로 고민하다 “루씬에서 계산하는 Score를 바꾸어 줄 수 있다면…” org.apache.lucene.search.function.* 패키지에서 해답을 찾았다!
CustomScoreQuery
org.apache.lucene.search.function.* : Programmatic control over documents scores.
org.apache.lucene.search.function
Class CustomScoreQuery
java.lang.Objectorg.apache.lucene.search.Query
org.apache.lucene.search.function.CustomScoreQuery
WARNING: The status of the search.function package is experimental. The APIs introduced here might change in the future and will not be supported anymore in such a case.
루씬내부에서 문서점수계산에 대한 API를 제공하고 있다!
사용법은 다음과 같다.
FeildCustomScoreQuery scoreQuery = new FeildCustomScoreQuery("필드명", FieldScoreQuery.Type.타입); query = new [CustomScoreQuery를 상속한클래스](query, scoreQuery);
CustomScoreQuery – 오버라이드하여 점수계산
public class FeildCustomScoreQuery extends CustomScoreQuery { .... @Override public float customScore(int doc, float subQueryScore, float valSrcScore){ return valSrcScore; } }
해당 CustomScoreQuery,FeildCustomScoreQuery를 사용하여 날짜가 최신순이면 스코어를 높이는 방식으로 최신순정렬을 해결하였다. 루씬내부에서 실험적인 시도이므로 다음릴리즈때 어떤변화가 있을지 모르겠지만, 개인적으로 정렬에 대한 근본적인접근이라 생각한다. org.apache.lucene.search.function.* 패키지 내부의 클래스를 활용하면 다음과 같은 멋진것도 가능하다.
- 구글의 PageRank 개념으로 문서랭킹을 만들 수 있다.
- 지도검색에서 어떤 상점을 검색시, 현재 위치한 곳에서 가까운 가게가 랭킹이 더 높다.
- 블로그검색에서 RSS구독자가 많은 블로그가 랭킹이 더 높다.
- 등등…
참고 URL
- http://lucene.apache.org/java/2_9_1/api/all/org/apache/lucene/search/function/CustomScoreQuery.html
- http://lucene.jugem.jp/?eid=92
- http://blog.mono-koubou.net/archives/6
- http://blog.mono-koubou.net/wp-content/uploads/2008/01/valuesourcequerysample.txt
