Elasticsearch에서 문장을 분석할 때, Token이 분리되는 시점에 따른 결과의 차이를 알아보자.
해당 글은 자동완성 서비스를 구축하던 중에, Highlighting옵션이 원하는대로 표현되질 않아서 따로 예제를 정리한다.
결론부터 말하자면, tokenizer에서 edge-ngram 분석이 먼저 이루어진 term은, offset의 정보가 term별로 다르게 나오게 된다. 이에 반해, filter에서 edge-ngram 필터가 이루어진 term은, 이미 whitespace로 offset이 정해진 채로 filter단계로 넘어오기 때문에, term이 쪼개져도 모두 같은 offset을 가지고 있게 된다.
위의 말을 간략하게 변경하자면, Elasticsearch의 Highlighting을 사용하게 되면, 색인과 검색어가 매칭된 부분이 태그로 감싸서 반환되게 되는데, 이때 offset을 이용하여 표시하게 된다.
즉, filter단계 edge-ngram을 사용하게 되면 입력한 부분만 표시가 되는것이 아닌, 그 단어의 전체가 태그로 감싸지게 된다
검색결과는 같지만, Highlighting의 결과가 달라지게 된다.
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"
}
}
}
}
POST test/_doc
{
"filter": "노바스코샤",
"tokenizer": "노바스코샤"
}
tokenizer 단계에서 edge-ngram을 수행한다. 이후 filter 단계에서 trim, lowercase를 처리한다.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
}
]
}
tokenizer 단계에서 whitespace를 수행한다. 이후 filter 단계에서 trim, lowercase를 수행하고 edge-ngram을 수행한다.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
}
]
}
실제 질의식으로 하이라이팅 옵션이 어떻게 되어있느지 확인하자
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의 하이라이팅의 결과가 다름을 알 수 있다. 이론적으로 아는것과, 실제로 이렇게 나오는 결과를 보니 좀더 명확하게 두개의 차이를 알 수 있게 되었다.