2013년 6월 27일 목요일

Solr query를 어떻게 만드는 것이 좋을까?

편의상 평어체를 사용합니다.

Solr query parser의 문제점

Solr에 포함된 대부분의 query parser(Lucene, Dismax, ExtendedDismax 등)는 사용자의 검색어를 한번에 분석(analyze)하는 것이 아니라 여러 조각으로 나눠서 분석한다. 예를 들면 다음과 같다.
"president obama" +havard +professor
검색 쿼리가 위와 같은 경우 query parser가 "president obama", +havard, +processor에 대해서 각각 analyzer를 실행한다. 영어처럼 단어마다 띄어쓰기를 하는 언어에서는 문제가 없는 방식이다. 하지만 최적의 형태소 결합을 찾는 mecab-ko-lucene-analyzer의 특성상 완전한 문장이 analyzer로 들어와야 정확한 형태소 분석이 보장된다. 두 가지의 방식으로 ‘이성과 감성'이라는 문장을 분석하면 다음과 같이 다른 결과가 나온다.
'이성과', '감성'을 각각 형태소 분석을 했을 경우, mecab-ko-dic 형태소 분석 결과
이(관형사), 성과(명사), 감성(명사) => 원하는 형태소 분석 결과가 아님
'이성과 감성'을 형태소 분석 했을 경우, mecab-ko-dic 형태소 분석 결과
이성(명사), 과(조사), 감성(명사)

해결 방안

모든 단어 검색을 할 경우

'+이성과 +감성'과 같이 모든 단어 포함 검색을 하고 싶은 경우, Sloppy phrase query를 사용한다. 위의 예에서도 알 수 있듯이, phrase query인 경우 완전한 구문이 analyzer로 넘어간다. 충분히 큰 slop 값을 주면 모든 문서에 대해서 검색할 수 있다. lucene query parser의 경우 다음과 같이 사용할 수 있다.
q=title:"이성과 감성"~1000

일부 단어 검색을 할 경우

phrase query를 쓰기 힘든 일부 단어 검색의 경우, 공백 문자 앞에 ,(쉼표)와 같은 특정한 기호를 넣는 꼼수로 문제를 해결했다. 다음과 같은 식이다.
q=title:(이성과, 감성)
여기서 쉼표는 텍스트가 이어지는 지점이라는 힌트를 은전한닢 형태소 분석기에 제공하는 역할을 한다. 실제로 '이성과'와 '이성과,’를 mecab-ko-dic을 통해 형태소 분석을 하면 다음과 같이 다른 결과가 나온다.
이성과
이    MM,F,이,*,*,*,*,*
성과  NN,F,성과,*,*,*,*,*
EOS
이성과,
이성  NN,T,이성,*,*,*,*,*
과    JC,F,과,*,*,*,*,*
,     SY,*,*,*,*,*,*,*
EOS
SY(Symbol)은 mecab-ko-lucene-analyzer에서 인덱스에 남기지 않기 때문에, 사용할 수 있는 꼼수이다. 이 방법에도 부작용이 있는 경우가 발견되면, 지금의 해결 법은 바뀔 수 있다.

샘플

title과 body로 구성된 데이터를 검색한다고 했을 때, 다음과 같이 sloppy query에 가중치를 주고 edismax를 사용하여, 일부 단어 검색을 하는 query를 만들 수 있다.
사용자 검색어가 ‘이성과 감성' 일 때,
title="이성과 감성"~1000^5.0 OR body="이성과 감성"~1000^3.0 OR _query_:"{!edismax qf='title^2.0 body' mm='2<75%'}이성과 감성"
이 쿼리식이 최적이라는 것이 아니라, 어디까지나 예제일 뿐입니다. 효과적인 쿼리식이나 괜찮은 예제가 있으면 댓글로 알려주시면 감사하겠습니다.

남겨진 문제

띄어쓰기 오류에서 오는 문제점

테스트 중 알게 된 예상치 못한 경우가 띄어쓰기가 없는데 여러 개의 token이 나오는 경우, 검색식이 만들어지는 방식이 좀 이상한 것이었다.
mecab-ko-lucene-analyzer의 Standard(Query|Index)Tokenizer에서는 어절에 가중치를 주기 위해서 어절 token을 유의어 개념으로 반환하는데 예를 들면 다음과 같다.
이성과 감성 => (이성과 이성) 감성
위에서보면 '이성과'와 '이성' 두 token을 position 변화 없이 (positionIncrement=0) 반환하는데, Solr의 debugQuery를 활용하여 보면, 띄어쓰기가 있는냐 없는냐에 따라, Solr에서 실제의 쿼리가 다르게 구성되는 것을 볼 수 있다.
title:(이성과, 감성) 분석 결과
"debug": {
  "rawquerystring": "title:(이성과, 감성)",
  "querystring": "title:(이성과, 감성)",
  "parsedquery": "((title:이성과 title:이성)/no_coord) title:감성",
  "parsedquery_toString": "(title:이성과 title:이성) title:감성",
...
}
위의 경우, '이성과'와 '이성'이 유의어 개념으로 올바르게 처리되고 있다. (parsedquery에서 괄호로 묶인 부분)
title:(이성과감성) 분석 결과
"debug": {
  "rawquerystring": "title:(이성과감성)",
  "querystring": "title:(이성과감성)",
  "parsedquery": "title:이성과 title:이성 title:감성",
  "parsedquery_toString": "title:이성과 title:이성 title:감성",
  ...
  }
‘이성과'와 ‘이성'이 유의어 개념이 아니라, 서로 다른 token으로 처리되고 있다.
Solr query parser가 왜 이런 결과를 내는지는 잘 모르겠다. 특별한 이유가 없는 한, 논리적으로 틀린 방식이라고 생각이 된다.
위의 문제를 해결 방안은 두 가지 정도로 생각된다.
  1. 사용자 검색어를 전처리를 통해 띄어쓰기 보정을 한 후에, Solr query를 구성
  2. 띄어쓰기 단위가 아니라, 형태소 분석기에서 나온 형태소 단위로 처리하는 query parser를 작성
일단 은전한닢 프로젝트에서는 작업양이 적게 느껴지는 첫 번째 방법을 통해서 문제를 해결할 수 있는지 조사하고 있다. 관련 작업이 끝나는 대로, 이 글을 수정하거나 새로운 글을 올릴 예정이다.
Written with StackEdit.

2013년 6월 9일 일요일

mecab-ko-lucene-analyzer-0.10.0 배포합니다.

mecab-ko-lucene-analyzer-0.10.0이 나왔습니다.
  • 복합명사 처리 로직 재작성
  • 기분석 사전 처리 로직 추가
  • StandardIndexTokenizerFactory에서 decompoundMinLength 속성 제거
  • StandardIndexTokenizerFactory에서 compoundNounMinLength 속성 추가. 해당 길이 보다 짧은 복합명사는 분해하지 않음. 기본값은 3.

주의: 이번 버전은 mecab-ko-dic-1.3.0 이상의 버전을 사용하셔야 합니다.

여기서 받으실 수 있습니다.

mecab-ko-dic-1.3.0-20130608 배포합니다.

mecab-ko-dic-1.3.0-20130608이 나왔습니다.
  • mecab-ko-lucene-analyzer-0.10.0  버전을 위해 사전 구조가 변경
    • 각 사전 단어에 인덱스 표현 필드 추가
    • Preanalysis 사전(기분석 사전) 추가
여기서 받으실 수 있습니다.