[백업][가리사니] 솔라 길이가 긴 단어의 제외하기
elasticsearch, lucene, solr

이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.

이 글은 솔라를 중심으로 설명되어있습니다. 루씬과 엘라스틱서치는 응용해서 사용하시면됩니다.

최근 어떤 커뮤니티에서 솔라에서 길이기 매우 긴 단어에 대한 질문이 올라와 팁을 작성해보겠습니다.

예전에 지인이 운영하는 유명 커뮤니티의 솔라 작업을 해줄때도 발생했던 문제였습니다. 단어를 조각내서 작업하는 검색엔진의 특성상 매우 긴 단어는 여러가지 토큰 처리중 문제가 발생할 수 있습니다. 단순히 해당 데이터가 검색이 안돼는 정도며 괜찮지만 오류를 내뱉기 때문에 반드시 처리해 줘야 하는 것중 하나입니다.

  • 사실 검색이 안돼는 걸 떠나서… 서버의 자원을 낭비시킵니다.

예를 들면 (물론 설정에따라 아래처럼 작동하지 않고 본인이 설정한 대로 작동할겁니다. 하나의 예제)

“가리사니 개발자공간” 이라는 단어가있다면 “가리사니” “개발자공간” 이렇게 2개로 쪼개진 상태에서 다시 “가리사니”와 “개발자공간”을 보게되고 “개발자공간”이라는 단어가 “개발자” + “공간” 이라는 사전내 검색되는 단어라고 판단하여 “가리사니” “개발자공간” “개발자” “공간” 이라는 분석을 하게됩니다. 하지만 띄어쓰기없이 수만단어가 써있을때는? 한글 형태소를 비롯하여 형태소마다 작업은 다르겠지만 합성어를 찾아 사전 분리 하는 작업에서 수만단어를 일일히 사전에 대조하고 찾는 과정을 합니다. 문제는 바로 여기서 발생하죠… 2만단어씩 띄어쓰기가 없다던지 하는 단어들은 사실상… 대부분은 유저들이 장난삼아 쓴 글이고 추론되는 단어의 존재 여부에 상관없이 단어를 자르는 필터들은 개고생을 하게됩니다. (이건 작동을 하더라도.. 서버의 자원입장에서 좋지 않습니다.)

<fieldType name="ko" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false">
	<analyzer>
		<filter class="solr.LowerCaseFilterFactory" />
		<tokenizer class="org.apache.lucene.analysis.ko.KoreanTokenizerFactory" />
		<filter class="org.apache.lucene.analysis.ko.KoreanFilterFactory" hasOrigin="true" hasCNoun="true" />
		<filter class="org.apache.lucene.analysis.ko.WordSegmentFilterFactory" hasOrijin="true" />
		<filter class="org.apache.lucene.analysis.ko.HanjaMappingFilterFactory" />
		<filter class="org.apache.lucene.analysis.ko.PunctuationDelimitFilterFactory" />
		<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
	</analyzer>
</fieldType>

예를들어 위와 같은 fieldType 이 있을때 어떤 유저가 “가나다라마바사(띄어쓰기없이 수만단어)”를 해두면 위에서 말한대로 Tokenizer 부터 서버는 개고생을 하다가 너무 오랜시간이 걸리면 해당 작업이 오류를 뱉고 정지되어버립니다.

그래서 단어를 제외해야합니다.

<fieldType name="ko" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false">
	<analyzer>
		<filter class="solr.LowerCaseFilterFactory" />
		<filter class="solr.LengthFilterFactory" min="1" max="50" />
		<tokenizer class="org.apache.lucene.analysis.ko.KoreanTokenizerFactory" />
		<filter class="org.apache.lucene.analysis.ko.KoreanFilterFactory" hasOrigin="true" hasCNoun="true" />
		<filter class="org.apache.lucene.analysis.ko.WordSegmentFilterFactory" hasOrijin="true" />
		<filter class="org.apache.lucene.analysis.ko.HanjaMappingFilterFactory" />
		<filter class="org.apache.lucene.analysis.ko.PunctuationDelimitFilterFactory" />
		<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
	</analyzer>
</fieldType>

바로 이부분입니다. 단어의 유효함은 최소 1글자 최대 50글자 라고 알려주는 겁니다. 사실 1글자는 상당히 애매한 편인데… 1글자 단어들이 생각보다 많습니다…. (검색엔진 고문하기…)

이렇게 됬을때 띄어쓰기가 없는 50글자 초과 단어는 제외됩니다. 필자는 한 단어가 띄어쓰기없이 50글자가 넘는건 해당 단어가 제외되어 검색되지 않더라도 문제가 없다고 생각합니다.

필자가 50이라고 설정한 이유는 띄어쓰기없이 50자 초과 사용한 단어가 과연 유효한 단어일까? 라는 생각입니다.

좀더 코드를 손본다면 다음과 같이 될 수 있습니다.

<fieldType name="ko" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false">
	<analyzer type="index">
		<filter class="solr.LowerCaseFilterFactory" />
		<filter class="solr.LengthFilterFactory" min="1" max="50" />
		<tokenizer class="org.apache.lucene.analysis.ko.KoreanTokenizerFactory" />
		<filter class="org.apache.lucene.analysis.ko.KoreanFilterFactory" hasOrigin="true" hasCNoun="true" />
		<filter class="org.apache.lucene.analysis.ko.WordSegmentFilterFactory" hasOrijin="true" />
		<filter class="org.apache.lucene.analysis.ko.HanjaMappingFilterFactory" />
		<filter class="org.apache.lucene.analysis.ko.PunctuationDelimitFilterFactory" />
		<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
	</analyzer>
	<analyzer type="query">
		<filter class="solr.LowerCaseFilterFactory" />
		<tokenizer class="org.apache.lucene.analysis.ko.KoreanTokenizerFactory" />
		<filter class="org.apache.lucene.analysis.ko.KoreanFilterFactory" hasOrigin="true" hasCNoun="true" />
		<filter class="org.apache.lucene.analysis.ko.WordSegmentFilterFactory" hasOrijin="true" />
		<filter class="org.apache.lucene.analysis.ko.HanjaMappingFilterFactory" />
		<filter class="org.apache.lucene.analysis.ko.PunctuationDelimitFilterFactory" />
		<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
	</analyzer>
</fieldType>

index, query 로 세분화 하였고 쿼리에는 별도의 랭스 필터를 처리하지 않았습니다. 일반적으로 검색어는 길어봐야 50문자 이하라고생각해서 입니다.

  • 물론 검색을 하는 서버 입장에서도 너무 긴단어는 검색되지 않도록 처리해줘야겠죠.

이렇게 하여 검색을 처리할 수 있습니다.

참고

LengthFilterFactory

  • https://lucene.apache.org/core/6_0_0/analyzers-common/org/apache/lucene/analysis/miscellaneous/LengthFilterFactory.html

추신

- 취업해야하는데 연락이 안온다 ㅠㅠ