Elasticsearch 분석 순서에 따른 차이

JunMyung Lee·2023년 2월 28일
0

Elasticsearch

목록 보기
13/33

Elasticsearch에서 문장을 분석할 때, Token이 분리되는 시점에 따른 결과의 차이를 알아보자.
해당 글은 자동완성 서비스를 구축하던 중에, Highlighting옵션이 원하는대로 표현되질 않아서 따로 예제를 정리한다.

Tokenizer에서의 Edge-ngram vs Filter에서의 Edge-ngram

결론부터 말하자면, tokenizer에서 edge-ngram 분석이 먼저 이루어진 term은, offset의 정보가 term별로 다르게 나오게 된다. 이에 반해, filter에서 edge-ngram 필터가 이루어진 term은, 이미 whitespace로 offset이 정해진 채로 filter단계로 넘어오기 때문에, term이 쪼개져도 모두 같은 offset을 가지고 있게 된다.

위의 말을 간략하게 변경하자면, Elasticsearch의 Highlighting을 사용하게 되면, 색인과 검색어가 매칭된 부분이 태그로 감싸서 반환되게 되는데, 이때 offset을 이용하여 표시하게 된다.

즉, filter단계 edge-ngram을 사용하게 되면 입력한 부분만 표시가 되는것이 아닌, 그 단어의 전체가 태그로 감싸지게 된다
검색결과는 같지만, Highlighting의 결과가 달라지게 된다.

예제

Settings & Mappings

PUT test
{
  "settings": {
    "analysis": {
      "analyzer": {
        "edge_ngram_tokenizer": {
          "type": "custom",
          "tokenizer": "suggest_tokenizer",
          "filter": [
            "trim",
            "lowercase"
          ]
        },
        "edge_ngram_filter": {
          "type": "custom",
          "tokenizer": "whitespace",
          "filter": [
            "trim",
            "lowercase",
            "suggest_filter"
          ]
        }
      },
      "filter": {
        "suggest_filter": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 48,
          "token_chars": [
            "letter"
          ]
        }
      },
      "tokenizer": {
        "suggest_tokenizer": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 48,
          "token_chars": [
            "letter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "filter": {
        "type": "text",
        "analyzer": "edge_ngram_filter",
        "search_analyzer": "standard",
        "term_vector": "with_positions_offsets_payloads"
      },
      "tokenizer": {
        "type": "text",
        "analyzer": "edge_ngram_tokenizer",
        "search_analyzer": "standard",
        "term_vector": "with_positions_offsets_payloads"
      }
    }
  }
}

PUT Document

POST test/_doc
{
  "filter": "노바스코샤",
  "tokenizer": "노바스코샤"
}

edge_ngram_tokenizer analyze test

  • tokenizer 단계에서 edge-ngram을 수행한다. 이후 filter 단계에서 trim, lowercase를 처리한다.
  • 예제 결과를 보면, start_offset과 end_offset의 위치가 0, 5의 값으로 동일한것을 알 수 있다.
    • 같은 Term으로 인식한다는 소리
GET test/_analyze
{
  "analyzer": "edge_ngram_filter",
  "text": "노바스코샤"
}
// Result
{
  "tokens" : [
    {
      "token" : "노바",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "노바스",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "노바스코",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "노바스코샤",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "word",
      "position" : 0
    }
  ]
}

edge_ngram_filter

  • tokenizer 단계에서 whitespace를 수행한다. 이후 filter 단계에서 trim, lowercase를 수행하고 edge-ngram을 수행한다.
  • 예제 결과를 보면, end_offset의 위치가 순차적으로 단어의 길이만큼 증가되고 있는것을 볼 수 있다.
GET test/_analyze
{
  "analyzer": "edge_ngram_tokenizer",
  "text": "노바스코샤"
}

//Result
{
  "tokens" : [
    {
      "token" : "노바",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "노바스",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "노바스코",
      "start_offset" : 0,
      "end_offset" : 4,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "노바스코샤",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "word",
      "position" : 3
    }
  ]
}

Query

실제 질의식으로 하이라이팅 옵션이 어떻게 되어있느지 확인하자

GET test/_search
{
  "query": {
    "multi_match": {
      "query": "노바",
      "fields": [
        "tokenizer", "filter"
        ]
    }
  },
  "highlight": {
    "fields": {
      "filter": {}, "tokenizer": {}
    }
  }
}

// Result
"hits" : [
  {
    "_index" : "test",
    "_type" : "_doc",
    "_id" : "sSrFloYBFNhpHduP_LIX",
    "_score" : 0.41501677,
    "_source" : {
      "filter" : "노바스코샤",
      "tokenizer" : "노바스코샤"
    },
    "highlight" : {
      "filter" : [
        "<em>노바스코샤</em>"
      ],
      "tokenizer" : [
        "<em>노바</em>스코샤"
      ]
    }
  }
]

filter의 하이라이팅과, tokenizer의 하이라이팅의 결과가 다름을 알 수 있다. 이론적으로 아는것과, 실제로 이렇게 나오는 결과를 보니 좀더 명확하게 두개의 차이를 알 수 있게 되었다.

0개의 댓글