Reference/How To ...

2. Mixing exact search with stemming

drscg 2018. 10. 5. 18:00

When building a search application, stemming is often a must as it is desirable for a query on skiing to match documents that contain ski or skis. But what if a user wants to search for skiing specifically? The typical way to do this would be to use a multi-field in order to have the same content indexed in two different ways:

검색 application을 만드는 경우, skiing 에 대한 query가 ski 나 skis 를 포함하는 document와 일치하는 것이 바람직하므로, 형태소 분석이 필수적이다. 그러나, 만약 사용자가 특별히 skiing 을 search하기를 원한다면 어떻게 해야 할까? 이를 위한 일반적인 방법은 동일한 내용을 2가지 다른 방법으로 index하기 위해, multi-field 를 사용하는 것이다.

PUT index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "english_exact": {
          "tokenizer": "standard",
          "filter": [
            "lowercase"
          ]
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "body": {
          "type": "text",
          "analyzer": "english",
          "fields": {
            "exact": {
              "type": "text",
              "analyzer": "english_exact"
            }
          }
        }
      }
    }
  }
}

PUT index/_doc/1
{
  "body": "Ski resort"
}

PUT index/_doc/2
{
  "body": "A pair of skis"
}

POST index/_refresh

With such a setup, searching for ski on body would return both documents:

이러한 설정을 사용하면, body 에서 ski 를 search하면, 2 document 모두 return된다.

GET index/_search
{
  "query": {
    "simple_query_string": {
      "fields": [ "body" ],
      "query": "ski"
    }
  }
}
{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped" : 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0.2876821,
    "hits": [
      {
        "_index": "index",
        "_type": "_doc",
        "_id": "2",
        "_score": 0.2876821,
        "_source": {
          "body": "A pair of skis"
        }
      },
      {
        "_index": "index",
        "_type": "_doc",
        "_id": "1",
        "_score": 0.2876821,
        "_source": {
          "body": "Ski resort"
        }
      }
    ]
  }
}

On the other hand, searching for ski on body.exact would only return document 1 since the analysis chain of body.exact does not perform stemming.

반면에, body.exact 에서 ski 를 search하면, body.exact 의 analysis chain에서 형태소 분석을 하지 않았기 때문에, document 1만 return된다.

GET index/_search
{
  "query": {
    "simple_query_string": {
      "fields": [ "body.exact" ],
      "query": "ski"
    }
  }
}
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped" : 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.2876821,
    "hits": [
      {
        "_index": "index",
        "_type": "_doc",
        "_id": "1",
        "_score": 0.2876821,
        "_source": {
          "body": "Ski resort"
        }
      }
    ]
  }
}

This is not something that is easy to expose to end users, as we would need to have a way to figure out whether they are looking for an exact match or not and redirect to the appropriate field accordingly. Also what to do if only parts of the query need to be matched exactly while other parts should still take stemming into account?

이는 정확한 일치를 찾는지 여부를 파악하는 방법을 가져야 하고 적절한 field로 redirect이 필요하기 때문에, 최종 사용자에게 쉽게 노출될 수 있는 것이 아니다. 또한 query의 일부만 정확히 일치되어야 하고, 다른 부분은 여전히 형태소 분석을 고려해야 한다면, 어떻게 해야 하나?

Fortunately, the query_string and simple_query_string queries have a feature that solve this exact problem: quote_field_suffix. This tell Elasticsearch that the words that appear in between quotes are to be redirected to a different field, see below:

다행이도, query_string 과 simple_query_string query는 이 문제를 정확히 해결하는 quote_field_suffix 기능이 있다. 이는 따옴표(quote) 사이에 나타나는 단어를 다른 field로 redirect해 주는 역활을 한다.

GET index/_search
{
  "query": {
    "simple_query_string": {
      "fields": [ "body" ],
      "quote_field_suffix": ".exact",
      "query": "\"ski\""
    }
  }
}
{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped" : 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.2876821,
    "hits": [
      {
        "_index": "index",
        "_type": "_doc",
        "_id": "1",
        "_score": 0.2876821,
        "_source": {
          "body": "Ski resort"
        }
      }
    ]
  }
}

In the above case, since ski was in-between quotes, it was searched on the body.exact field due to the quote_field_suffix parameter, so only document 1 matched. This allows users to mix exact search with stemmed search as they like.

위의 경우에서, ski 는  따옴표(quote) 사이에 있으므로, quote_field_suffix parameter 로 인해, body.exact field에서 search되어, document 1만 일치한다. 이를 통해 사용자는 정확한 search와 원하는 형태소 분석을 혼합할 수 있다.

'Reference > How To ...' 카테고리의 다른 글

5. Tune for search speed  (0) 2018.10.05
4. Tune for indexing speed  (0) 2018.10.05
3. Getting consistent scoring  (0) 2018.10.05
1. General recommendations  (0) 2018.10.05
How to  (0) 2018.10.05