ES 검색엔진 만들기 (9) multifield search와 multifield search 검색 향상
주소 검색 multifield match 를 사용하면 되잖아??
multifield search의 강점과 내가 이를 사용하지 못한 이유!!!에 대한 포스팅
들어가기 전에
일반적인 매치 쿼리는 단일 필드에서 검색을 위한 쿼리이다. 필드 매핑을 이해하고, 필드를 위한 적절한 analyzer를 선택할 수도 있다. 그리고 모든 워드를 매칭할수도 있다. 원한다면 or and match 그리고 minimum_should_match 등을 이용하여 매칭 워드의 비율을 조절하는것 또한 가능하다. 또한 fuzzy 매칭을 사용하여 근접한 워드에 대한 검색 또한 가능하다. (한글은 조금 다름) 다시 말해 매치 쿼리는 굉장히 유연하고, 파워풀 하다
다방면의 검색은 굉장히 까다롭고 선택하기 어렵다. 그리고 사람이 딱딱 필드를 지정해주는것이 아닌 상황의 일상 대화에서는 !!! 그렇기 떄문에 더욱 더 잘 알아야 하고 잘 써야 한다.
단순히 검색이 될만한 모든 필드를 지정하고 request 를 날리면 되는것이 아니라는말이다
multi_match_query
엘라스틱에서 지원하는 multi_match query를 보자
{
"multi_match": {
"query": "여기 서울시 마포구 서교동 GS25 인데요",
"fields": [ "name", "sido", "sigun", "gu", "law_address_dong", "admin_address_dong", "road_address_name"]
}
}
크으,,, 주소 검색엔진 개발 끝이다?? 여기다 각 필드의 analyzer를 지정해주면 토크나이징까지 해주니 완벽하다.
현실은 그러하지 못하다. 보이는 예가 다가 아니다. 나는 multi_match query의 작동 법을 알고 있음에도 종종 이것들을 잘못 사용하고, 끔직한 결과를 불러올떄가 많다.
일단은 이 muㅣti_match query의 올바른 사용법 부터 익힌후 검색엔진을 개선해 나아가 보도록 하자
Types of multi-field search
- best_fields
- most_fields
- cross_fields
- phrase
- bool_prefix
Type 1.best_fields
{
"multi_match": {
"query": "quick brown fox",
"fields": [ "title", "body" ],
"type": "best_fields" # default
}
}
는 dis_max query 와 동일한 기능을 한다.
{
"dis_max": {
"queries": [
{ "match": { "title": "quick brown fox" }},
{ "match": { "body": "quick brown fox" }}
]
}
}
dis_max 는 루씬의 disjunctionMaxQuery 에서 따온 용어이다. 예를 들어 멀티 키워드 검색시 여러 필드에서 검색을 수행하게 되는 조건에서 멀티 키워드의 키워드와 동일한 키워드 score를 더욱 높이 평가하는 방식을 제공한다.
이게 말로는 힘드니 예를 들어 설명해보겠다. 내가 검색하고 싶은건 서교동에 있는 “수원 왕갈비” 집이다.
내가 만약 “수원 왕갈비”를 서치하였을때 어디가 더 높은 점수를 부여 받을까?
\\\\ 1번 \\\\
{
"sido" : "수원시"
"name" : "수원 냉면"
"branch" : "수원대학교점"
}
\\\\ 2번 \\\\
{
"sido" : "서울시"
"name" : "수원 왕갈비"
"branch" : "서교동점"
}
다를수도 있지만, 일반적으론 1번의 키워드 빈도가 많아 relevance가 높게 나온다. 그러면 “수원 왕갈비”에 더 높은 점수를 부여 시킬수 있는 방법은 없을까?. 이럴때 사용하는 것이 dis_max 이다.
multi_match 에서는 디폴트 타입이 best_field로 위에서 말한 dis_max기능이 이루어진다.
다른 필드들을 완전히 무시할수도 있지만, tie_breaker 를 통해 일정 가중치를 부여하는것 또한 가능하다.
Most matching fields
종종 우리는 한 텍스트를 여러 analyzer를 통해 인덱싱할 때가 있다. 아마도 근접 매칭 또는 자동완성을 위한 edge-ngram 을 사용하거나 할때 사용할것이다.
이러한 케이스에선 우리는 모든 필드에 대한 쿼리하고 각 일치 항목의 스코어를 합하여 가장 일치하는 필드가 있는 문서를 찾게 된다.
아래위 쿼리는 동일한 쿼리이다.
{
"bool": {
"should": [
{ "match": { "title": "quick brown fox" }},
{ "match": { "title.stemmed": "quick brown fox" }},
{ "match": { "title.synonym": "quick brown fox" }},
{ "match": { "title.shingle": "quick brown fox" }},
{ "match": { "title.edge_ng": "quick brown fox" }}
]
}
}
{
"multi_match": {
"query": "quick brown fox",
"fields": [ "title", "title.*" ],
"type": "most_fields"
}
}
Cross field matching
우리는 종종 다양한 필드에 펼쳐진 객체들을 검색하게 된다. 예를들면 “John Smith” 같이 user object 안에있는 first_name, last_name 등을 검색할떄? 이러한 케이스에서 우리는 가능한 많은 개별 단어를 찾고자 한다. most_field type을 통해 해결할수 있을것 같지만, 차이점이 몇가지 존재한다.
- most_field type 은 필드마다 operator, minimum_should_match 를 적용하지만 cross_fileds type 은 term 마다 적용디 된다.
- 관련성. cross_fields type은 입력한 키워드를 분석한 후 모든 field 에 대해 각각 term을 검색한다.
그럼 cross field 는 어떨때 유리할까? cross_fields type 은 여러 field가 일치헤야 하는 구조화된 문서에 유용하다.
cross field type 은 다음과 같이 검색을 진행한다.
- query 분석기로 분석한다.
- 분석된 각 각의 term들은 모든 Field에 대해 질의하는데, 이 질의 대상이 되는 Field들을 1개의 큰 Field 로 만들어 match 되는지 확인한다.
Multi_match 가중치와 wildcards
Multi_match는 wildcard를 지원한다.
GET /_search
{
"query": {
"multi_match" : {
"query": "수원 왕갈비",
"fields": [ "name^3", "*_address" ]
}
}
}
위 쿼리에서는 wildcard를 통해 _address로 끝나는 모든 field애 대해 검색을 진행한다. 또한 상호명인 name에 3배의 가중치를 부여 하여 검색한다.