본문 바로가기

Lucene

[Lucene] DrillDown Facet Search in Lucene 4.6.1

앞선 포스트에서 FacetSearch에 대해 간단하게 알아보았습니다.


http://devyongsik.tistory.com/679


앞의 예제에서 카테고리를 "java"에서 "programming/java"와 같은 형태로 상위레벨의 카테고리를 같이 표현하면

어떻게 될까요?


한번 테스트를 돌려보면 아래와 같은 exception이 발생합니다.


java.lang.IllegalArgumentException: delimiter character '/' (U+2f) appears in path component "programming/lucene"


"/" 이 캐릭터가 문제가 된다는 것을 대략 알 수 있는데요.. 앞에 예제의 결과에서 색인시 CategoryPath를 생성할때 

CategoryPath cp = new CategoryPath("category", categories[i]);

와 같이 첫번째 파라메터로 넘긴 "category"가 결과에 자동으로 붙어서 "category/lucene"과 같이 나왔던 것을 기억하실겁니다.


결국, 색인시 "category/lucene"으로 색인하고, facet search를 할 때 

new CountFacetRequest(new CategoryPath("category"), 10) 

와 같이 CountFacetRequest를 생성하고 이 결과로 "lucene 3, java 4" 형태의 결과가 나오는 것인데요.


CategoryPath cp = new CategoryPath("category22", categories[i]);


이 과정에서 categories[i]의 값에 '/'가 포함될 경우 Exception이 발생하는 것을 알 수 있습니다.


그럼 일단, 이 부분을 해결하고 검색을 수행하는 샘플 코드를 보겠습니다.



1. category 값의 수정

우선 테스트를 위해 카테고리의 값을 수정하였습니다. 앞에 java혹은 ruby 카테고리를 상위 카테고리로 붙여넣었고 구분자는 "/"를 사용하였습니다. ruby/hadoop이라는 요상한 카테고리가 있지만 테스트를 위해 임의로 넣은 값이니 그냥 넘어가주세요. :)


2. 색인

TaxonomyWriter와 FacetFields를 사용한 추가색인은 동일합니다. 다만, CategoryPath를 생성하는 부분에서 약간의 

차이가 있습니다.

CategoryPath cp = new CategoryPath("category/" + categories[i], '/');

부분인데요..

맨 앞에 facetSearch를 위한 "category/"를 임의로 추가하고, 

맨 마지막 파라메터로 구분자를 넣어주는 형태입니다.


위의 코드는 아래의 코드로 대체될 수 있습니다.

CategoryPath cp = new CategoryPath("category", "java", "lucene");

즉, 구분자를 넘겨주는 것이 아니라 미리 값을 구분해서 넘겨주는 형태입니다.


3. 검색

검색 역시 많은 부분이 동일하지만, 검색을 위한 CategoryPath를 생성하는 부분에서 다소 차이가 있습니다.


CategoryPath drillDownCategoryPath = new CategoryPath("category" + "/" + "java", '/');

CategoryPath drillDownCategoryPath2 = new CategoryPath("category" + "/" + "ruby", '/');


List<FacetRequest> countFacetRequests = Lists.newArrayList();
countFacetRequests.add(new CountFacetRequest(new CategoryPath("category"), 10));
countFacetRequests.add(new CountFacetRequest(drillDownCategoryPath, 10));
countFacetRequests.add(new CountFacetRequest(drillDownCategoryPath2, 10));

를 보시면 되는데요, 간단하게 생각하시면 여기서 생성한 CategoryPath의 바로 다음 레벨까지만 facetSearch가 된다고 생각하시면 됩니다.
예를 들어서 위의 코드를 아래와 같이 수정해보면..

List<FacetRequest> countFacetRequests = Lists.newArrayList();
countFacetRequests.add(new CountFacetRequest(new CategoryPath("category"), 10));

결과는 아래와 같이 나옵니다.

## category: 2
category/java: 4
category/ruby: 2

category의 바로 다음 레벨의 값으로면 facet search가 되었죠. 하지만 우리가 원하는 것은 "java"와 "ruby"밑의 카테고리별의 facet search결과입니다. 때문에 그것을 위해서 위와 같이 CategoryPath를 추가로 생성하여 주고 있습니다.

4. 쿼리
앞선 포스트의 예제해서는 단순히 MatchAllDocsQuery를 사용하여 검색을 하였지만, 이 예제에서는 조금 다릅니다.
DrillDownQuery q = new DrillDownQuery(facetSearchParams.indexingParams, new MatchAllDocsQuery());
와 같이 쿼리를 생성하여 검색을 수행합니다.
MatchAllDocsQuery가 기본 쿼리로 검색을 수행하고, 그 결과셋을 가지고 CategoryPath에 의한 facetSearch가 진행됩니다.

5. 결과
## category: 2
category/java: 4
category/ruby: 2
## category/java: 3
category/java/lucene: 2
category/java/hadoop: 1
category/java/java: 1
## category/ruby: 2
category/ruby/hadoop: 1
category/ruby/ruby: 1