오랜만에 Lucene 포스트입니다.
FacetSearch의 샘플 코드인데요.. 기능 자체는 예전 버전부터 제공이 되었던 기능입니다.
FacetSearch란 무엇이냐 하면 간단하게 SQL의 group by와 같은 기능이라고 생각하시면 됩니다.
보통 쇼핑몰에서 검색결과를 카테고리별로 묶어서 해당 카테고리에 몇개의 상품이 검색되어 있는지등을
보여줄때 많이 사용됩니다.
그리고, DB도 그렇지만 성능에 많은 영향을 미치는 기능이기도 하지요..
11번가에서 검색 운영을 할때도 이런저런 성능적인 이슈들이 많았지만 그중 검색결과 페이지의 로딩 속도에 큰 영향을 주는 부분은 바로 이 group by 기능이었습니다.
예전부터 Lucene의 이 기능을 한번 사용해보고 싶었는데 이제서야 학습테스트 코드로나마 작성해보네요.. --ㅋ
깊이있게 들여다보지 못 하고 단순히 색인/검색을 할 수 있는 SampleCode를 보면서 간략히 설명을 드리겠습니다.
FacetSearch는 검색을 위해 색인하였던 인덱스를 사용하지않고 별도의 디렉토리를 생성합니다.
위 예제에서는 taxonomyDirectory가 그 역할을 하게됩니다. RAMDirectory가 아니라면 실제로는 원래의 색인 디렉토리와는 다른 물리적 디렉토리의 경로가 들어가게 될것입니다.
또한, taxonomyDirectory를 사용해 TaxonomyWriter를 생성합니다.
같은 경로나 혹은 같은 RAMDirectory를 사용하지 않는다는 것을 봐주시면 되고요.. 이 두가지 instance를 만들어내면 색인 준비는 완료가 됩니다.
2. 색인
색인을 할때는 FacetFields라는 클래스의 객체를 생성합니다. 4.0.X 버전에서는 CategoryDocumentBuilder 클래스를 사용했던것 같은데, 4.6.1버전에서는 이 클래스 대신 FacetFields 클래스를 사용합니다.
색인은 색인을 하게 될 모든 Document에 대해서 일반적인 색인외에 아래의 추가적인 과정을 진행합니다.
1) List<CategoryPath>의 객체를 생성하고..
2) CategoryPath 객체를 생성합니다. 이때, 필드명과 해당 필드의 value가 생성자로 사용됩니다. ("category", categories[i])
3) 2)에서 생성한 categoryPath를 1)에서 생성한 List에 add합니다. grouping할 필드가 더 있다면 1),2)를 반복합니다.
4) 3)에서 만들어진 List를 facetFields에 document와 함께 add합니다.
5) 모든 Document에 대해 위 작업이 끝나면, taxnomyWriter의 commit()과 close()를 실행합니다.
3. 검색
색인에서 FacetSearch를 위한 추가과정이 있었듯이 검색에서도 추가과정이 있습니다.
우선 IndexSearcher외에 TaxonomyReader를 생성합니다.
검색을 위한 Query객체를 생성한 후 FacetSearchParams 객체를 생성합니다.
그리고, FacetsCollector를 생성하여, MultiCollector를 사용해 indexSearcher의 search메서드를 실행합니다.
추측컨데 이 MultiCollector에 의해서 한번의 검색으로 TopScoreDocCollector와 FacetsCollector에 검색결과가 수집되는듯 합니다.
4. 결과
실제 테스트 메서드를 실행한 결과는 아래와 같습니다.
category/hadoop, 2.0
category/lucene, 2.0
category/ruby, 1.0
category/java, 1.0
5. 추가
사실 위 내용에서 약간의 거짓말을 한부분이 있습니다. 색인시 CategoryPath를 만들때 제일 첫번째 파라메터가 groupby할 필드의 필드명이라고 했는데 실은 그렇지는 않습니다.
아래와 같이 작성하셔도됩니다.
CategoryPath cp = new CategoryPath("category22", categories[i]);
다만, 이 경우 검색시에 FacetSearchParams 파라메터를 아래와 같이 생성하여 주시면 됩니다.
FacetSearchParams facetSearchParams = new FacetSearchParams(new CountFacetRequest(new CategoryPath("category22"), 10));
결과는 아래와 같습니다.
category22/hadoop, 2.0
category22/lucene, 2.0
category22/ruby, 1.0
category22/java, 1.0
혹시 뭔가 느껴지시나요? 카테고리앞에 category, 혹은 category2가 붙어있는것을 보실 수 있습니다.
그렇다면 카테고리가 혹시 "programming/java" , "programming/ruby" 등과 같이 depth를 가지고 있다면 어떻게 될까요? 이 부분은 다음에 작성 할 포스트인 DrillDownQuery를 사용하여 해결해보도록 하겠습니다.