DevOps/Elastic Stack

[Elasticsearch] wildcard 쿼리에 대해 제대로 이해하기 - 컴도리돌이

컴도리돌이 2024. 11. 7. 09:07
728x90

RDBMS를 사용하다가 Elasticsearch에서 쿼리를 작성하면서 wildcard를 사용한 쿼리의 결과가 제가 생각했던 것과 달라서, 해당 내용에 대해 충분히 이해하기 위해 글을 작성하려고 합니다. Wildcard query를 작성하면서 예상했던 결과는 RDBMS의 LIKE에서 사용했던 '%searchInput%'와 같은 형태가 가능할 것으로 기대했는데, 막상 확인되는 결과는 그렇지 않았습니다.
 
원본 텍스트는 "경기도 용인시 기흥구 중동 1100번지"와 같은 형태의 주소입니다. 제가 시도한 쿼리는 다음과 같았습니다.

{
  "query": {
    "bool": {
      "must": [
        {
          "wildcard": {
            "address": {
              "value": "*경기도 기흥구*"
            }
          }
        }
      ]
    }
  }
}

 
그런데 결과는 아래와 같이 hits count가 0이었습니다. 😂

{
  "took" : 15,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

Elasticsearch에서 Wildcard 쿼리의 작동 방식

그렇다면 Elasticsearch에서 사용하는 wildcard query는 무엇이 다를까요? 🤔
 
Elasticsearch에서의 wildcard query는 term level query입니다. Term level query란 무엇일까요? 😟
 

Term-level queries | Elasticsearch Guide [8.15] | Elastic

Term-level queries still normalize search terms for keyword fields with the normalizer property. For more details, see normalizer.

www.elastic.co

Term query는 inverted index를 기준으로 결과를 찾는다는 의미이며, 이는 분석(analyzed)된 term keyword가 있어야 함을 의미합니다. 문서를 색인할 때 토크나이징된 단어가 아니라면 inverted index에 포함되지 않기 때문에, 아예 비교 대상에 포함되지 않는 것입니다.
 
제가 테스트한 custom analyzer의 토크나이징은 아래와 같았습니다.

POST /test_index/_analyze
{
  "field": "address",
  "text": "경기도 용인시 기흥구 중동 1100번지"
}

 

토크나이징 결과

{
  "tokens" : [
    {
      "token" : "경기도",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "<HANGUL>",
      "position" : 0
    },
    {
      "token" : "용인시",
      "start_offset" : 4,
      "end_offset" : 7,
      "type" : "<HANGUL>",
      "position" : 1
    },
    {
      "token" : "기흥구",
      "start_offset" : 8,
      "end_offset" : 11,
      "type" : "<HANGUL>",
      "position" : 2
    },
    {
      "token" : "중동",
      "start_offset" : 12,
      "end_offset" : 14,
      "type" : "<HANGUL>",
      "position" : 3
    },
    {
      "token" : "1100번지",
      "start_offset" : 15,
      "end_offset" : 21,
      "type" : "<ALPHANUM>",
      "position" : 4
    }
  ]
}

 
토크나이징된 결과에 *경기도 기흥구*라는 단어는 없기 때문에, 처음 질의한 wildcard 결과가 hits가 0일 수밖에 없었던 것입니다. 🫠


올바른 Wildcard 쿼리 작성 방법

따라서, 올바른 쿼리를 작성하기 위해서는 특정 토큰을 기준으로 조회해야 합니다. 

{
  "query": {
    "bool": {
      "must": [
        {
          "wildcard": {
            "address": {
              "value": "*경기도*"
            }
          }
        },
        {
          "wildcard": {
            "address": {
              "value": "*기흥구*"
            }
          }
        }
      ]
    }
  }
}

 
Wildcard 쿼리는 성능이 좋지 않을 수 있으며, 특히 앞에 와일드카드가 있는 경우(예: *경기도) 전체 문서 검색을 수행하게 되어 시간이 오래 걸릴 수 있으므로, 쿼리의 효율성을 높이기 위해서는 검색할 토큰을 명확히 설정하고, 필요에 따라 데이터에 적합한 사용자 정의 분석기를 설정하여 검색 성능을 개선하는 것이 중요합니다.