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 사전(기분석 사전) 추가
여기서 받으실 수 있습니다.

2013년 4월 27일 토요일

mecab-ko-dic-1.2.0-20130427 배포합니다.

mecab-ko-dic-1.2.0-20130427이 나왔습니다.
  • mecab-ko에 맞춰서 학습 규칙 및 설정 변경
  • 인명사전(NN-Person.csv) 데이터 다량 추가
  • _Wikipedia.csv 사전을 단일 명사와 복합 명사 사전으로 나눔
여기서 받으실 수 있습니다.

mecab-ko-0.9.0 배포합니다.

mecab-ko 소개

mecab-ko는 은전한닢 프로젝트에서 사용하기 위한 MeCab의 fork 프로젝트 입니다. 최소한의 변경으로 한국어의 특성에 맞는 기능을 추가하는 것이 목표입니다.

0.9.0 버전에서는 공백 문자(white space)를 포함하는 특정 품사의 비용을 늘리는 기능이 추가되었습니다. mecab-ko-dic-1.2.0 버전부터 mecab-ko를 지원합니다.

보다 자세한 내용은 https://bitbucket.org/eunjeon/mecab-ko에서 확인하실 수 있습니다.

2013년 4월 12일 금요일

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

mecab-ko-lucene-analyzer-0.9.5가 나왔습니다.
  • jar 패키지를 mecab-ko-mecab-loader.jar와 mecab-ko-lucene-analyzer.jar로 분리. (mecab-ko-mecab-loader.jar는 JNI 클래스를 포함하므로 System classpath에 위치해야 함)
  • 위의 사항과 관련하여 README.md를 변경
여기서 받으실 수 있습니다.
이전의 버전은 JNI 클래스 패키지가 분리되어있지 않아서, collection을 하나 이상 만들 수 없는 문제점이 있습니다. 꼭 새로운 버전을 사용하시기 바랍니다. 관련하여 설치 방법도 변경됐으니, https://github.com/eunjeon/mecab-ko-lucene-analyzer/blob/master/README.md를 다시 확인 해주시기 바랍니다.

2013년 4월 11일 목요일

mecab-ko-lucene-analyzer OpenJDK에서 사용하기

mecab-ko-lucene-analyzer가 OpenJDK 상에서는 제대로 동작을 하지 않는 문제가 발견되었습니다. 정확히는 MeCab의 JNI가 비정상적인 동작(형태소 분석 결과가 나오지 않음)을 합니다. MeCab은 Java 인터페이스를 만들기 위해서 SWIG라는 툴을 사용하는데, 이 문제가 SWIG의 문제인지 OpenJDK의 문제인지는 잘 모르겠습니다.

이것저것 시도해보다가 다음과 같이 mecab-java-0.996의 Makefile에서 gcc 최적화 옵션을 -O나 -O1으로 낮추면 정상 동작하는 것을 확인하였습니다. 뭔가 매우 찝찝하지만 일단은 여기까지 입니다. -_-a

테스트 된 환경은 다음과 같습니다.
CentOS 6.3
java version "1.6.0_24"
OpenJDK Runtime Environment (IcedTea6 1.11.1) (rhel-1.45.1.11.1.el6-i386)
OpenJDK Server VM (build 20.0-b12, mixed mode)
관련하여 더 좋은 해결 방법을 아시는 분은 알려주시면 감사 하겠습니다.

TARGET=MeCab
JAVAC=javac
JAVA=java
JAR=jar
CXX=c++
INCLUDE=/usr/lib/jvm/java-6-openjdk/include
PACKAGE=org/chasen/mecab
LIBS=`mecab-config --libs`
INC=`mecab-config --cflags` -I$(INCLUDE) -I$(INCLUDE)/linux
all:
    $(CXX) -O3 -c -fpic $(TARGET)_wrap.cxx  $(INC)
    $(CXX) -O1 -c -fpic $(TARGET)_wrap.cxx  $(INC)
    $(CXX) -shared  $(TARGET)_wrap.o -o lib$(TARGET).so $(LIBS)
    $(JAVAC) $(PACKAGE)/*.java
    $(JAVAC) test.java
    $(JAR) cfv $(TARGET).jar $(PACKAGE)/*.class
test:
    env LD_LIBRARY_PATH=. $(JAVA) test
clean:
    rm -fr *.jar *.o *.so *.class $(PACKAGE)/*.class

cleanall:
    rm -fr $(TARGET).java *.cxx