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