[Elasticsearch] 부분 일치 (4) - 인덱스 기간ngrams 및 인덱스 기간의 실시간 검색 최적화

이 장은 Elasticsearch 공식 안내서의 Partial Matching
한 장.

인덱스 기간 최적화(Index-time Optimizations)


현재 우리가 토론하는 모든 방안은 조회 기간에 있다.특별한 매핑이나 색인 모드(Indexing Patterns)가 필요하지 않습니다.그것들은 단지 색인에 이미 존재하는 데이터 위에서 간단하게 일할 뿐이다.
검색 기간의 유연성은 대가가 있다. 검색 성능이다.때때로 이런 대가를 조회 이외의 곳에 두는 것은 가치가 있다.실시간 웹 응용 프로그램에서 100밀리초의 추가 지연은 감당하기 어렵다.
색인 기간에 데이터를 준비하면 검색을 더욱 유연하고 효율적으로 할 수 있습니다.당신은 여전히 대가를 치렀다. 증가된 색인 크기와 약간 낮은 색인 흡수량, 그러나 이 대가는 색인 기간에 지불하는 것이지, 모든 검색의 실행 기간에 지불하는 것이 아니다.
당신의 사용자는 당신에게 감사할 것입니다.

부분 일치(Partial Matching)의ngrams


우리는 "거꾸로 색인에 존재하는 단어만 찾을 수 있다"고 말했다.비록prefix,wildcard,regexp 조회는 위의 표현이 정확하지 않다는 것을 증명하지만, 하나의 단어를 기반으로 하는 조회를 실행하는 것은 단어 목록을 훑어보는 것보다 일치하는 단어를 얻는 것이 더 빠르다는 것은 의심할 여지가 없다.일부 일치를 위해 데이터를 미리 준비하면 검색 성능을 높일 수 있습니다.
색인 기간에 준별 데이터는 정확한 분석 체인(Analysis Chain)을 선택하는 것을 의미하며, 우리가 선택한 도구의 일부분을 n-gram이라고 부른다.n-gram은 단어의 슬라이딩 창으로 상상할 수 있습니다.n은 길이를 나타냅니다.만약 우리가 단어 quick에 대해 n-gram을 얻게 된다면, 결과는 선택한 길이에 달려 있다.
길이 1(unigram): [q, u, i, c, k]
길이 2(bigram): [qu,ui,ic,ck]
길이 3(trigram): [qui, uic, ick]
길이 4(four-gram): [quic, uick]
길이 5(five-gram): [quick]
단순한 n-grams는 일치하는 단어의 어떤 부분에 유용하며, 복합 단어인ngrams에서 우리는 그것을 사용할 것이다.그러나 실시간 검색에 있어서 우리는 가장자리 n-grams(Edge n-grams)라고 불리는 특수한 n-grams를 사용했다.가장자리 n-grams는 단어의 시작점에 시작점을 놓습니다.단어 quick의 가장자리 n-gram은 다음과 같습니다.
q
qu
qui
quic
quick
사용자가'quick'을 검색할 때의 입력 형식을 따랐다는 것을 알 수 있습니다.즉각적인 검색에 있어서는 완벽한 단어라는 얘기다.

인덱스 중 실시간 검색(Index-time Search-as-you-type)


색인을 만드는 동안 즉시 검색의 첫 번째 단계는 분석 체인(Analysis Chain)을 정의하는 것입니다(구성 해석기에서 논의한 것). 여기서 우리는 이러한 절차를 상세하게 설명할 것입니다.

색인 준비


첫 번째 단계는 사용자 정의 edge_ngram 단어 필터, 우리는 그것을 autocomplete_라고 부른다filter:
{
    "filter": {
        "autocomplete_filter": {
            "type":     "edge_ngram",
            "min_gram": 1,
            "max_gram": 20
        }
    }
}

상기 설정의 역할은 이 단어 필터가 받아들인 모든 단어에 대해 최소 길이가 1, 최대 길이가 20인 가장자리ngram (Edgengram) 을 생성합니다.
그리고 이 단어 필터를 사용자 정의 해상도에 설정합니다. 이 해상도의 이름은 autocomplete입니다.
{
    "analyzer": {
        "autocomplete": {
            "type":      "custom",
            "tokenizer": "standard",
            "filter": [
                "lowercase",
                "autocomplete_filter" 
            ]
        }
    }
}

이상의 해석기는 표준분사기를 사용하여 문자열을 독립된 단어로 구분하여 소문자 형식으로 만들고 가장자리ngrams를 생성합니다. autocomplete_filter.
색인을 만들고, 단어 필터와 해상도에 대한 전체 요청은 다음과 같습니다.
PUT /my_index
{
    "settings": {
        "number_of_shards": 1, 
        "analysis": {
            "filter": {
                "autocomplete_filter": { 
                    "type":     "edge_ngram",
                    "min_gram": 1,
                    "max_gram": 20
                }
            },
            "analyzer": {
                "autocomplete": {
                    "type":      "custom",
                    "tokenizer": "standard",
                    "filter": [
                        "lowercase",
                        "autocomplete_filter" 
                    ]
                }
            }
        }
    }
}

다음 analyze API를 통해 올바른 행동을 확인할 수 있습니다.
GET /my_index/_analyze?analyzer=autocomplete
quick brown

반환된 단어는 해상도가 정상적으로 작동하고 있음을 나타냅니다.
q
qu
qui
quic
quick
b
br
bro
brow
brown
이를 사용하려면 업데이트-맵핑 API를 통해 필드에 적용해야 합니다.
PUT /my_index/_mapping/my_type
{
    "my_type": {
        "properties": {
            "name": {
                "type":     "string",
                "analyzer": "autocomplete"
            }
        }
    }
}

이제 테스트 문서를 색인해 보겠습니다.
POST /my_index/my_type/_bulk
{ "index": { "_id": 1            }}
{ "name": "Brown foxes"    }
{ "index": { "_id": 2            }}
{ "name": "Yellow furballs" }

이 필드 질의


"brown fo"에 대한 간단한 match 조회를 사용한다면:
GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "name": "brown fo"
        }
    }
}

두 문서가 일치하는 것을 발견할 수 있습니다. 설령 Yellow furballs가 브라운도 포함하지 않고 fo도 포함하지 않는다 하더라도:
{

  "hits": [
     {
        "_id": "1",
        "_score": 1.5753809,
        "_source": {
           "name": "Brown foxes"
        }
     },
     {
        "_id": "2",
        "_score": 0.012520773,
        "_source": {
           "name": "Yellow furballs"
        }
     }
  ]
}

validate-query API를 통해 다음 문제를 발견합니다.
GET /my_index/my_type/_validate/query?explain
{
    "query": {
        "match": {
            "name": "brown fo"
        }
    }
}

검색은 검색 문자열의 모든 단어의 가장자리ngrams를 찾습니다.
name:b name:br name:bro name:brow name:brown name:f name:fo
name:f라는 조건은 두 번째 문서를 만족시켰습니다.furballs는 f,fu,fur 등으로 인덱스되었기 때문입니다.그래서 이상의 결과를 얻은 것도 이상할 게 없다.자동complete 해상도는 색인 기간과 검색 기간에 동시에 적용되며, 통상적으로 이것은 모두 정확한 행위이다.그러나 현재의 장면은 많지 않은 이 규칙을 사용해서는 안 되는 장면 중의 하나이다.
역렬 인덱스에 모든 단어의 가장자리ngrams가 포함되어 있는지 확인해야 하지만, 사용자가 입력한 전체 단어 (brown과fo) 와 일치합니다.우리는 색인 기간에 autocomplete 해상도를 사용할 수 있고, 검색 기간에 표준 해상도를 사용하여 이 목적을 달성할 수 있다.쿼리에서 직접 해석기를 지정하는 것은 검색 기간 분석기를 변경하는 방법입니다.
GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "name": {
                "query":    "brown fo",
                "analyzer": "standard" 
            }
        }
    }
}

또한name 필드의 맵에서 각각 index_를 지정할 수 있습니다analyzer 및 search_analyzer.왜냐하면 저희는 그냥 Search를 수정하고 싶어서_analyzer, 따라서 데이터 색인을 다시 하지 않는 전제에서 맵을 수정할 수 있습니다.
PUT /my_index/my_type/_mapping
{
    "my_type": {
        "properties": {
            "name": {
                "type":            "string",
                "index_analyzer":  "autocomplete", 
                "search_analyzer": "standard" 
            }
        }
    }
}

이 때 validate-query API를 통해 다음과 같은 설명을 얻을 수 있습니다.
name:brown name:fo
질의를 반복하면 Brown foxes라는 문서만 얻을 수 있습니다.
대부분의 작업이 색인 기간에 끝났기 때문에 검색에 필요한 것은 두 개의 단어만 찾는 것입니다: 브라운과 포, 이것은 match_를 사용하는 것보다phrase_prefix는 fo로 시작하는 모든 단어를 찾는 데 더욱 효율적입니다.
제안 완료(Completion Suggester)
가장자리ngrams를 사용하여 만든 실시간 검색은 간단하고 유연하며 신속합니다.그러나 때때로 그것은 여전히 빠르지 않다.지연의 영향은 무시할 수 없다. 특히 실시간 피드백을 제공해야 할 때.때때로 가장 빠른 검색 방식은 검색이 없는 것이다.
ES의 완성 제안은 완전히 다른 해결 방안을 채택했다.완료 가능한 전체 목록 (Possible Completions) 을 제공하여 유한 상태 변환기 (Finite State Transducer) 를 만듭니다. 이 변환기는 그림 (Graph) 을 설명하는 데 최적화된 데이터 구조입니다.제안 사항을 검색하기 위해 ES는 그림의 시작 부분에서 시작하여 사용자에게 일치하는 경로(Matching Path)를 따라 문자를 입력하여 이동합니다.사용자 입력이 검증되면 현재 경로에 따라 가능한 모든 건의가 발생합니다.
이 데이터 구조는 메모리에 존재하기 때문에 접두사 검색은 그 어떤 단어를 기반으로 하는 검색보다 빠르다.이를 사용하여 이름과 브랜드(Names and Brands)를 자동으로 완성하는 것은 좋은 선택이다. 왜냐하면 그들은 통상적으로 특정한 순서로 조직하기 때문이다. 예를 들어'Johnny Rotten'은'Rotten Johnny'라고 쓰지 않기 때문이다.
단어의 순서가 쉽게 예측되지 않을 때, 가장자리ngrams는 건의를 완성하는 것보다 더 좋은 방안이다.

가장자리ngrams 및 우편 번호


테두리ngrams라는 기술은 구조화된 데이터에 사용될 수 있다. 예를 들어 본 장에서 언급한 우편 번호 등이다.물론postcode 필드는 not_ 대신 analyzed로 설정되어야 할 수도 있습니다analyzed, 하지만 우편 번호에 키워드 분사기를 사용해서 not_analyzed 필드와 같습니다.
TIP
keyword 분사기는 아무런 행위 (no-operation) 가 없는 분사기입니다.그것이 받아들인 문자열은 그대로 단어로 출력됩니다.그래서 어떤 것들은 보통 not_로 여겨진다analyzed 필드는 일부 처리 (예를 들어 소문자로 변환) 가 필요한 경우 유용합니다.
이 예는 키워드 분사기를 사용하여 우편번호 문자열을 문자 흐름으로 변환하기 때문에 가장자리ngram 단어 필터를 사용할 수 있습니다.
{
    "analysis": {
        "filter": {
            "postcode_filter": {
                "type":     "edge_ngram",
                "min_gram": 1,
                "max_gram": 8
            }
        },
        "analyzer": {
            "postcode_index": { 
                "tokenizer": "keyword",
                "filter":    [ "postcode_filter" ]
            },
            "postcode_search": { 
                "tokenizer": "keyword"
            }
        }
    }
}

좋은 웹페이지 즐겨찾기