About
이미 이전 포스트의 multi_match
에서 fuzziness
패러미터를 이용하는 법을 간단히 소개한 적이 있다. 하지만 그때는 그냥 fuzziness
값으로 ‘auto’를 넣고 사용하는 법만 알려주었는데 원하는 결과를 얻기위해서는 조금 더 이해할 필요가 있다는 것을 필자도 최근에 알게됐다. 그래서 이번 글을 통해서는 fuzzy search
에 대해서 조금 더 살펴본 것을 정리해보도록 하겠다.
글은 대부분 Elasticsearch 공식 문서를 번역한 것임을 미리 밝혀둔다.
만약 Elasticsearch와 python의 elasticsearch-dsl 패키지에 대해 잘 모르는 분들은 아래의 글들을 먼저 보고 오는 것을 추천한다.
Elasticsearch의 기본 개념과 Python 패키지 elasticsearch-dsl 기본 사용법 Python 패키지 elasticsearch-dsl 기본 사용법 Part 2 Search 결과를 요청한 순서대로 Sorting하여 Return 받는법
Damerau-Lavenshtein vs Levenshtein
fuzzy query가 기본적으로 사용하게되는 알고리즘은 Damerau-Lavenshtein distance
이다. 간단하게는 a=‘ax’, b=‘axe’라는 두가지의 문자열이 있을 때, a와 b의 유사도(거리)를 재는 방법이다. 몇개의 문자를 더하고, 빼고, 대체해야하는지를 계산하는 방법으로 위의 예를 이용하면 ‘ax’와 ‘axe’는 ‘e’를 하나만 빼는 걸로 같아질 수 있기 때문에 거리가 1이 되는 것이다.
기본으로 사용되는 알고리즘이 Damerau-Lavenshtein distance
이지만, 원하면 Levenshtein distance
도 사용할 수 있다. 차이점은 전자는 전위(순서 바꾸기)가 가능하지만 후자는 불가능하다는 점이다. 예를 들어서 ‘aex’와 ‘axe’를 비교한다고 해보자. 전자를 사용하게되면 e와 x의 순서만 바꾸면 되므로 거리는 1이되고 후자를 사용하게 되면 먼저 e를 빼고 다시 뒤에 e를 더하게 되므로 거리가 2가 된다. 이 의미는 후자를 이용할 때는 ‘aex’와 ‘axe’의 거리가 ‘faxes’와 ‘axe’의 거리와 같게 된다. 그러한 이유로 직관성이 더 떨어지기 때문에 대부분의 경우에는 Damerau-Lavenshtein distance
를 사용하게 된다.
경우에 따라서는 Levenshtein distance
를 사용해야 하는 경우가 있으니 아래에서 변경하는 방법을 알려주도록 하겠다.
Fuzzy search
Fuzzy search를 이해하기 위해서는 먼저 elasticsearch가 analyzer를 통과하게 된다는 점이다. 만약 analyzer에 대해서 처음 접하는 분이라면
자세한 설명을 읽기 힘든 분은 fuzzy search를 사용할 경우엔 해당 field는 default analyzer를 사용하라는 것만 기억하면 되겠다.
어쨋든 자세히 공식 문서의 예를 살펴보도록 하자. 먼저 이해해야 하는 부분은
text = 'The quick for jumped over the lazy dog.'
를 Snowball Analyzer
를 이용해서 elasticsearch로 저장하려고 하면 실제로 저장하는 값은 위에서 볼 수 있듯이, quick
, fox
, jump
, over
, lazi
, dog
이며, 검색을 할 때면 실제 저장된 문장이 아닌 위와 같은 terms
중에서 찾게 된다는 점이다. 그러다보니 fuzzy query를 실행하게 되면 query text가 우리가 예상치 못한 단어와 비교한 분석 결과를 보여주기도 한다. 같은 이유로 원문에는 등장하지 않았던 유사어나 동의어를 이용해서도 찾을 수 있는 경우가 생긴다.
위의 이미지의 예제로 돌아가면, 검색하려는 문구로 lazzy
를 넣었을 때 Snowball analyzer를 먼저 통과하고 lazzi
로 analyzed query로 검색을 시작하게 된다. 이 경우에는 lazzi
와 lazi
가 1의 거리에 있으므로 찾는 것을 성공하게 되는 것이다.
Implementation
복잡한 내용은 이 정도로 두고 먼저 코드를 살펴보도록 하자.
import elasticsearch
from elasticsearch_dsl import Search, Q, Index
# 먼저 elasticsearch 객체를 생성한다.
es = elasticsearch.Elasticsearch('localhost:9200')
s = Search(using=es, index='index_name')
s = s.query(
'multi_match',
query=name,
fuzziness=2,
fuzzy_transpositions=False,
fields=['item_name', 'brand_name']
)
res = s.execute()
fuzziness
의 값은 최대 거리를 의미한다. 2로 해놓으면 3이상 거리가 차이나는 것은 결과로 돌려주지 않는다.
공식 문서에서는 성능 문제상 1이나 2까지만 사용할 것을 권장하므로 더 큰 값을 사용하는 것은 주의하자.
fuzzy_transpositions
가 위에서 설명했던Levenshtein distance
를 사용하도록 하는 방법이다. default는 True로 들어가있어서 기본적으로는Damerau-Lavenshtein distance
를 사용하게 된다.
Conclusion
기억해야 할 점은 3가지다.
fuzziness
값은 1, 2 중에 하나로 설정하도록 하자.fuzzy_transpositions
를 이용하면 edit distance 알고리즘을 변경할 수 있다.fuzzy query
를 사용해서 검색하게 될 field는 간단한 analyzer(그냥 default를 사용하면 되는 듯하다)를 사용하도록 하자.
이번 글에서는 간단하게 match query + fuzziness option
에 대해서만 살펴봤는데, fuzzy query
, fuzzy_like_this/fuzzy_like_this_field
, suggesters
등의 방법으로 query를 날리는 방법도 있다. 이 방법들에 대해서는 다른 블로그에서 설명을 이어가도록 하겠다.