Elasticsearch multi_match 기본 사용법 및 Type에 대한 설명 정리

문서 목적


이 문서는 Elasticsearch에서 Multi_match query를 사용할 경우 필요한 지식에 대해 정리하고 공유하기 위해 작성했다. 가독성을 위해 Elasticsearch는 ES로 표기한다. 

테스트 환경


테스트 환경은 다음과 같다. 

  • ES, Kibana: 7.1 

  • MacOS Catalina 10.15.2

Multi_match 기본 설정에 대한 설명


아래와 같이 Query를 실행할 경우 기본값으로 설정되는 항목에 대해 설명한다. 

{
    "query": {
        "multi_match" : {
            "query":    "this is a test",
            "fields": [ "subject", "message" ]
        }
    }
}

기본값으로 설정되는 항목은 다음과 같다. 

  • Query 대상 필드 개수 제한: 1024개. 참고문서

  • Type: Best_fields

Multi_match wildcards, 가중치 설명


Multi_match는 Wildcards를 지원한다. 다음은 예시이다. 예시에서는 “*_name”로 Field를 지정해 _name으로 끝나는 Field를 대상 Field로 지정한다. 

GET /_search
{
  "query": {
    "multi_match" : {
      "query":    "Will Smith",
      "fields": [ "title", "*_name" ] 
    }
  }
}

또한, 각 각의 Field에 대해 가중치를 부여할 수 있다. 다음은 예시이다. 예시에서는 “subject” field는 “message” field에 대해 3배 중요하다. 

GET /_search
{
  "query": {
    "multi_match" : {
      "query" : "this is a test",
      "fields" : [ "subject^3", "message" ] 
    }
  }
}

Multi_match Type option에 대한 설명


다음은 Multi_match type과 해당 Type에 대한 설명을 표로 정리한 것이다. 

   
best_fields (기본 설정값) 모든 Field에 대해 Match되는 Field를 찾는다. 가장 잘 Match되는 1개의 Field를 기준으로 Score를 계산한다. 참고문서 best_fields.
most_fields 모든 Field에 대해 Match되는 각 각의 field에 대해 점수를 계산하고 모두 합한다. 합한 점수를 field 개수로 나눠서 최종 스코어를 계산한다. 참고문서 most_fields.
cross_fields Treats fields with the same analyzer as though they were one big field. Looks for each word in anyfield. See cross_fields.
phrase Runs a match_phrase query on each field and uses the _score from the best field. See phrase and phrase_prefix.
phrase_prefix Runs a match_phrase_prefix query on each field and uses the _score from the best field. See phraseand phrase_prefix.
bool_prefix Creates a match_bool_prefix query on each field and combines the _scorefrom each field. See bool_prefix.

Best_fields type

Best_fields type은 기본 설정값이며 모든 Field에 대해 Match되는 Field를 찾는다. Best_fields type는 dis_max query를 여러 개의 field에 대해 각 각 Match query한 결과와 같다.

예시는 다음과 같다. 

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "brown fox",
      "type":       "best_fields",
      "fields":     [ "subject", "message" ],
      "tie_breaker": 0.3
    }
  }
}

위의 예시 Query는 다음과 같이 실행될 것이다. 

GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "match": { "subject": "brown fox" }},
        { "match": { "message": "brown fox" }}
      ],
      "tie_breaker": 0.3
    }
  }
}

Best_fields type은 가장 잘 Match되는 1개의 Field를 기준으로 Score를 계산한다. tie_breaker 설정 시 다음과 같이 점수가 계산된다.

  • 가장 잘 Match되는 1개의 Field를 기준으로 Score를 계산한다.

  • 나머지 Field에 대해 tie_breaker * score를 한 후 Score에 Plus한다. 

참고

Best_fields Type Query 시 “tie_breaker”를 1로 설정하면, Most_fields Type으로 “tie_braker”를 설정 하지 않았을 경우와 같은 Score가 나오게 된다.

이 이유는 점수 계산 방식의 차이인데, Best_fields는 각 field의 Score 중 제일 큰 값을 최종 Score로 하는 반면에 Most_fields는 각 field의 Score를 모두 더해서 최종 Score를 계산하기 때문이다.

따라서, Best_fields Type의 “tie_braker”를 1로 설정할 경우 모든 Field의 Score를 합해 최종 Score를 계산하게 되므로 Most_fields Type과 동일한 점수가 나오게 된다.

점수 계산에 대해 조금 더 자세히 설명하자면, Best_fields type은 각 각의 Field에 대해 Field별로 BM25의 알고리즘을 이용해 점수를 계산한다. 

즉, 주어진 대상 Field 별로 Score를 계산하고 이 계산된 모든 Score를 비교해 가장 큰 값을 최종 Document의 Score로 결정하는 것이다. 다음은 예시이다. 

  "hits" : {
    "total" : {
      "value" : 4,
      "relation" : "eq"
    },
    "max_score" : 0.9913396,
    "hits" : [
      {
        "_shard" : "[test_multi_match_query][0]",
        "_node" : "_MqXQL7fShunzdcE4D6j9w",
        "_index" : "test_multi_match_query",
        "_type" : "_doc",
        "_id" : "QfQRHnABGReuIwC7G8rY",
        "_score" : 0.9913396,
        "_source" : {
          "first_field" : "first",
          "second_field" : "first",
          "third_field" : "first"
        },
        "_explanation" : {
          "value" : 0.9913396,
          "description" : "max of:",
          "details" : [
            {
              "value" : 0.6103343,
              "description" : "weight(second_field:first in 1) [PerFieldSimilarity], result of:",
              "details" : {...}
            },
            {
              "value" : 0.30873197,
              "description" : "weight(first_field:first in 1) [PerFieldSimilarity], result of:",
              "details" : {...}
            },
            {
              "value" : 0.9913396,
              "description" : "weight(third_field:first in 1) [PerFieldSimilarity], result of:",
              "details" : {...}
            }
          ]
        }
      },
...하략

이 예시는 Multi_match query를 Best_fields type으로 3개의 Field에 대해 Query 했을 때 계산 과정을 설명한 내용이다. 이 Multi_match query의 Query절은 3개의 Field에 대해 정확히 일치한다. (Ex. 검색어: First, 대상 필드 3개의 값: First)

예시 중 중요한 부분은 “hits” → ”hits” → ”_explanation” -> “details” 부분이다. 해당 부분을 보면 각 각의 3개의 Field에 대해 Per field 별로 Score를 계산하고 그 중 최고점을 최종 Score로 계산한다. 모두 Query 절이 정확히 일치해도 모든 문서의 대상 Field가 얼마나 자주 등장하는 지, Term의 길이 등 Relevance 계산 알고리즘에 의해 점수가 다르게 계산된다. 

공식 문서는 best_fields를 참고한다. 

중요 Operator and minimum_should_match에 대한 설명

Best_fields type, most_fields type은 공통적으로 Field 중심적이다. 이 두 개의 Type은 각 각의 Field에 대해 Match query를 실행하며 Match query마다 Operator와 Minimum_should_match가 적용된다. 

예시는 다음과 같다. 

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Will Smith",
      "type":       "best_fields",
      "fields":     [ "first_name", "last_name" ],
      "operator":   "and" 
    }
  }
}

위의 Query는 Will, Smith로 분석된 Token이 First_name, Last_name 중에 1개의 field라도 모두 존재하면 Match된다. 식으로 표현하면 다음과 같다. 

(+first_name:will +first_name:smith) 
| (+last_name:will  +last_name:smith)

정리하면, Query를 분석한 Terms가 1개의 Field에 대해 모두 존재할 경우 Match된다. 

Most_fields type

모든 Field에 대해 Match되는 각 각의 field에 대해 점수를 계산하고 모두 합한다. 

다음은 예시이다. 

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "quick brown fox",
      "type":       "most_fields",
      "fields":     [ "title", "title.original", "title.shingles" ]
    }
  }
}

위와 같이 작성된 쿼리는 실제로 다음과 같은 쿼리로 실행될 것이다.

GET /_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title":          "quick brown fox" }},
        { "match": { "title.original": "quick brown fox" }},
        { "match": { "title.shingles": "quick brown fox" }}
      ]
    }
  }
}

위 예시는 Title이라는 이름의 field를 다른 분석기를 통해 분석된 결과에 Match 되는 지 확인하고 점수를 계산하기 때문에 위 예시 Query의 검색 키워드인 “quick brown fox”에 더 근접한 결과를 얻을 수 있다.

점수 계산 방법은 다음과 같다. 

  • 각 각의 Match되는 field에 대해 점수를 계산한 후 합한다.

자세한 사항은 most_fields를 참고한다.

Phrase and Phrase_prefix type

phrase, Phrase_prfix type은 Best_fields type과 비슷하게 동작한다. 다른 점은 Match query 대신 Match_phrase, Match_phrase_prefix으로 동작한다는 것이다. 

자세한 사항은 Phrase, Phrase_prefix를 참고한다. 

Cross_fields type

Cross_fields type은 여러 field가 일치해야 하는 구조화된 문서에 특히 유용하다. 예를 들어, “Will Smith” 키워드로 First_name과 Last_name에 대해 Query할 경우, 최적의 Match는 1개 Field에 “Will”, 나머지 1개의 Field가 “Smith”에 Match될 경우일 것이다. 

Cross_fields type은 다음과 같이 검색을 진행한다.

  1. Query를 분석기로 분석한다.

  2. 분석된 각 각의 Term들을 모든 Field에 대해 질의하는데, 이 질의 대상이 되는 Field들을 1개의 큰 Field로 만들어 Match 되는 지 질의한다. 

중요 Most_field type 차이점 비교

Most_field type과의 차이점을 비교하면 2가지가 있다. 

첫 번째는 Most_field type은 field마다 Operator, minimum_should_match를 적용하지만 Cross_fields type은 Term마다 적용한다.

두 번째는 관련성이다. 만약 “Will Smith”이라는 이름을 검색한다고 생각해보자. 검색 시 ‘Will’, ‘Smith’이라는 두개의 Terms는 각 각의 last_name, first_name Field에 대해 검색할 것이다. 결과는 ”Smith Jones”가 “Will Smith” 보다 점수가 높을 것이다.

왜냐하면, last_name에 “Smith”는 굉장히 흔하지 않기 때문에 Last_name: “Smith”의 Score는 Last_name: “Will”, First_name: “Smith”의 총합 Score보다 더 높기 때문이다.

Cross_fields type은 query로 입력한 키워드를 분석한 후 모든 Field에 대해 각 각의 Term을 검색한다. 예시는 다음과 같다. 

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Will Smith",
      "type":       "cross_fields",
      "fields":     [ "first_name", "last_name" ],
      "operator":   "and"
    }
  }
}

이 Query는 다음과 같이 실행될 것이다.

+(first_name:will last_name:will) 
+(first_name:smith last_name:smith)

따라서, 최소한 1개 이상의 Field에 대해 모두 Terms가 존재할 것이다.

참고 자료 


참고 자료는 다음과 같다. 

댓글남기기