본문 바로가기

Lucene

[about Lucene] 루씬으로 검색엔진 개발하기 Query와 Filter

이번 포스트에서는 루씬에서 사용되는 QueryFilter의 기본적은 사용예제를
보여드리려고 합니다.

다시 예전에 작성했던 IndexSearcherTest의 메서드들을 살펴보면
4가지 쿼리 타입에 대해서 테스트 케이스가 작성되어 있는 것을 보실 수 있습니다.

  IndexSearcherTest.java

각 메서드를 간단하게 살펴보면 우선 searchByTerm 메서드에서는
Term을 사용하여 Query를 만들고 있습니다.

Term t = new Term("ids", "1");
Query q = new TermQuery(t);


위 쿼리는 ids 필드에서 값이 1인 Document를 검색하고 싶다는 뜻입니다.

searchByBooleanQuery 메서드에서는 Term을 사용한 Query 2개를 BooleanQuery를 사용하여
연결하고 있습니다.
 

BooleanQuery resultQuery = new BooleanQuery();
Term t = new Term("ids", "1");
Query q = new TermQuery(t);
resultQuery.add(q, Occur.SHOULD);
Term t2 = new Term("contents", "ibatis");
Query q2 = new TermQuery(t2);
resultQuery.add(q2, Occur.SHOULD);



위 예제는 ids가 1 이거나 contentsibatis 키워드가 들어가 있는 Document를 찾겠다는 뜻 입니다.
두개의 쿼리를 연결 할 때 Occur.SHOULD가 사용되는데 이것은 두 쿼리를 OR로 연결하겠다는 뜻 입니다.
이외에 MUST (and)MUST NOT(and not) 등이 있습니다.

searchByTermRangeQuery 메서드에서는 TermRangeQuery를 사용한 예입니다.
titles2h ~ j 사이 (첫 알파벳 기준)에 있는 Document를 검색합니다. 이때 대상 filedIndexing은 되어야 하고
Analyze는 되어있지 않아야 합니다.

Query q = new TermRangeQuery("titles2", "h", "j", true, true);
TopDocs docs = indexSearcher.search(q, 10);



searchByNumericRangeQuery 메서드는 NumericRangeQuery를 사용한 예 입니다.

Query q = NumericRangeQuery.newIntRange("price", 2000, 4000, true, true);



여기서는 price2000 ~ 4000 사이의 Document를 검색합니다. 맨 끝 boolean 파라메터 2개는 2000과 4000을 포함 할 것인지여부입니다. true가 포함하는 것 입니다.

NumericRangeQuery 쿼리의 static 메서드는 newIntRange말고도 LongRange등 여러 종류의 메서드가 제공되는데
한가지 주의 하실 점이 이 쿼리를 사용하시려면 색인시에 해당 필드의 타입을 맞춰주어야 합니다.
위 예제로 보면 IntRange를 사용하고 있기 때문에 색인시에 위 필드는 아래와 같이 색인 되어야 합니다.

NumericField numField = new NumericField("price", Field.Store.YES, true);
numField.setIntValue(prices[i]);



물론, TermRangeQuery처럼 Indexing은 되어 있어야하고, Analyze는 되어있지 않아야 합니다. (숫자이니 딱히 Analyze 할 건 없겠죠..)

추가적으로 Term쿼리와 Boolean쿼리에 대해서 말씀을 드리려고 합니다.

색인시 whiteanalyzer에 의해서 "lucene in action"의 문장이 [lucene]+[in]+[action]으로 분석되어
색인이 되었는데 검색시 Term t = new Term("title", "lucene in action"); 과 같은 형식으로
쿼리가 만들어지게 되면 절대로 검색이 되지 않을 것 입니다.

일반적으로 사용자가 입력 한 키워드를 스페이스로 구분하여 두개의 TermQuery를 생성하고
이를 BooleanQueryAND 조건으로 조합하면 키워드에 대해서는 괜찮은 쿼리식을 만들 수 
있습니다. 다만, 띄어쓰기를 하지 않는 사용자도 있기 때문에 이 부분에 대해서는
Analyzer를 활용하여 쿼리를 만들어 볼 수도 있습니다.

BuildQueryTest.java

보시면 사용자가 입력 한 키워드에 대해서 Analyzer로 분석을 하여 Boolean 쿼리를 
생성하고 있습니다. 이런 방식으로 키워드를 추출하여 검색 쿼리를 만드는 방법도 있으니
참고하시면 좋을 것 같습니다.

검색쿼리에서 Boolean쿼리를 사용하여 원하는 결과 값만을 가져 올 수 있지만
루씬에서는 Filter 클래스를 사용하여 마찬가지로 원하는 결과를 추려낼 수 있습니다.

FilterTest.java

메서드가 2개가 있는데요 하나는 Term에 의한 필터링
하나는 Range를 사용한 필터링입니다. Boolean 쿼리에 의한 필터링과 가장 큰 차이점은
필터는 점수에 대한 관여를 하지 않는다는 것 입니다.

관련 내용은
http://stackoverflow.com/questions/3721020/lucene-what-is-the-difference-between-query-and-filter
여기를 보시면 아실 수 있을 것 입니다.

필터를 사용 할 때 주의 할 부분이 있는데요
Term에 의한 필터링을 할 때는 해당 필드의 데이터가 NOT_ANALYZED로
되어 있어야 값이 정상적으로 나옵니다.

그리고 Range에 의한 필터는
해당 필드가 같은 NumericType으로 색인이 되어있어야
정상적으로 결과를 가져 올 수 있습니다.

RangeFilter에서 맨 끝의 boolean 파라메터 두개는
경계값을 가져 올 것인지 말 것인지에 대한 boolean 값입니다. true가 값을
포함하여 가져오도록 되어있습니다.

책을 보시면 CustomFilter를 직접 개발하여 사용하는 예제도 있으니
관심이 있으시다면 꼭 한번 보시기를 권해드립니다.

쿼리와 필터를 적절히 사용하시면 검색 식을 구현하는데 큰 무리가 없으실 것이구요..
검색에 있어서 가장 중요한 것은 아무튼 색인시에 얼마나 키워드가 잘 추출되었는가와
사용자가 입력한 키워드로부터 색인시 추출 되었던 키워드를 얼마나 잘 추출하여 쿼리식을
구성하는지 입니다.

보시면 아시겠지만 API 자체가 워낙 잘 구성이 되어있기 때문에
실제 사용에 있어서 큰 어려움은 없으실 것이라 생각됩니다.

예제는
https://github.com/need4spd/aboutLucene
에서 체크아웃 받으 실 수 있습니다.