概要
ElasticsearchのPrefix Query を使用する時に詰まったところをまとめました。
環境
- Elasticsearch 2.3.2
通常の利用方法
マッピング・データ用意
マッピングを設定します。
項目 | 設定値 |
---|---|
インデックス名 | test |
タイプ名 | internet |
フィールド名 | name |
で設定します。
$ curl -XPUT 'localhost:9200/test' -d ' { "mappings": { "internet": { "_all": { "enabled": false }, "dynamic": false, "properties": { "name": { "type": "string", "index": "not_analyzed" } } } } } '
データを用意します。
$ curl -s -XPOST localhost:9200/test/_bulk -d ' {"index": {"_type": "internet", "_id": "1"}} {"name": "fate"} {"index": {"_type": "internet", "_id": "2"}} {"name": "refactor"} {"index": {"_type": "internet", "_id": "3"}} {"name": "facebook"} '
検索クエリ
fa
で検索します。
$ curl localhost:9200/test/internet/_search?pretty -d ' { "query": { "prefix": { "name": { "value": "fa" } } }, "size": 5, "from": 0 } '
結果
"hits" : { "total" : 2, "max_score" : 1.0, "hits" : [ { "_index" : "test", "_type" : "internet", "_id" : "1", "_score" : 1.0, "_source" : { "name" : "fate" } }, { "_index" : "test", "_type" : "internet", "_id" : "3", "_score" : 1.0, "_source" : { "name" : "facebook" } } ] }
fate
とfacebook
がヒットしました。スコアは両方共1.0
です。感覚的に問題無いですね。
Normalize、ひらがな、カタカナの扱い
通常の検索データを用意する際は、表記ゆれを吸収するためにNormalizeであったり、ひらがな→カタカナの変換を行います。
しかしながらPrefix Queryはnot_analyzed
で検索クエリを投げるので以下のケースでヒットしなくなります。
事前準備
あらかじめicu-analyzerをインストールしておきます。
# ./bin/plugin install analysis-icu
マッピング・データ用意
使用するtokenizerはkeyword analyzer
です。これはnot_analyzed
と同じように使えるので、「analyzeする必要はないけど、Normalizeとかはさせたい」といった時に使えます。
項目 | 設定値 |
---|---|
インデックス名 | katakana |
タイプ名 | internet |
フィールド名 | name |
で設定します。
$ curl -XPUT 'localhost:9200/katakana' -d ' { "settings": { "analysis": { "analyzer": { "kana_analyzer": { "tokenizer": "keyword", "filter": [ "icu_normalizer", "asciifolding", "kana_filter" ] } }, "filter": { "kana_filter": { "type": "icu_transform", "id": "Hiragana-Katakana" } } } }, "mappings": { "internet": { "_all": { "enabled": false }, "dynamic": false, "properties": { "name": { "type": "string", "analyzer": "kana_analyzer" } } } } } '
データを登録します。
$ curl -s -XPOST localhost:9200/katakana/_bulk -d ' {"index": {"_type": "internet", "_id": "1"}} {"name": "あいうえお"} {"index": {"_type": "internet", "_id": "2"}} {"name": "かきくけこ"} {"index": {"_type": "internet", "_id": "3"}} {"name": "あいぱっど"} '
検索クエリ
あい
で検索します。感覚的にはあいうえお
やあいぱっど
がヒットして欲しいです。
$ curl localhost:9200/katakana/internet/_search?pretty -d ' { "query": { "prefix": { "name": { "value": "あい" } } }, "size": 5, "from": 0 } '
結果
"hits" : { "total" : 0, "max_score" : null, "hits" : [ ] }
ヒットしませんでした。検索クエリのあい
は、ひらがなのまま渡されるので、インデックスにカタカナ変換されたデータとマッチしません。
検索クエリ(カタカナ)
逆に言うとクエリに投げる時にカタカナに変換しておけばヒットします。
$ curl localhost:9200/katakana/internet/_search?pretty -d ' { "query": { "prefix": { "name": { "value": "アイ" } } }, "size": 5, "from": 0 } '
結果
"hits" : { "total" : 2, "max_score" : 1.0, "hits" : [ { "_index" : "katakana", "_type" : "internet", "_id" : "1", "_score" : 1.0, "_source" : { "name" : "あいうえお" } }, { "_index" : "katakana", "_type" : "internet", "_id" : "3", "_score" : 1.0, "_source" : { "name" : "あいぱっど" } } ] }
ちゃんとヒットしました。
N-gramにアナライズしたフィールド
N-gramなどでアナライズした場合はどうなるのでしょうか?
感覚的にはヒットして欲しいですが、スコアがどうなるかなどが直感的にわかりにくそうです。
マッピング・データ用意
項目 | 設定値 |
---|---|
インデックス名 | ngram |
タイプ名 | internet |
フィールド名 | name |
で設定します。
$ curl -XPUT 'localhost:9200/ngram' -d ' { "settings": { "analysis": { "analyzer": { "ngram_analyzer": { "tokenizer": "ngram_tokenizer" } }, "tokenizer": { "ngram_tokenizer": { "type": "nGram", "min_gram": "2", "max_gram": "3", "token_chars": [ "letter", "digit" ] } } } }, "mappings": { "internet": { "_all": { "enabled": false }, "dynamic": false, "properties": { "name": { "type": "string", "analyzer": "ngram_analyzer" } } } } } '
データを登録します。
$ curl -s -XPOST localhost:9200/ngram/_bulk -d ' {"index": {"_type": "internet", "_id": "1"}} {"name": "あいうえお"} {"index": {"_type": "internet", "_id": "2"}} {"name": "かきくけこ"} {"index": {"_type": "internet", "_id": "3"}} {"name": "あいぱっど"} '
検索クエリ
$ curl localhost:9200/ngram/internet/_search?pretty -d ' { "query": { "prefix": { "name": { "value": "あい" } } }, "size": 5, "from": 0 } '
結果
"hits" : { "total" : 2, "max_score" : 1.0, "hits" : [ { "_index" : "ngram", "_type" : "internet", "_id" : "1", "_score" : 1.0, "_source" : { "name" : "あいうえお" } }, { "_index" : "ngram", "_type" : "internet", "_id" : "3", "_score" : 1.0, "_source" : { "name" : "あいぱっど" } } ] }
ちゃんとヒットしました。スコアはやはり1.0
ですね。
ちなみにn-gramは2~3文字で設定したので、それ以上の文字数を投げると当然ヒットしません。
検索クエリ(長い)
$ curl localhost:9200/ngram/internet/_search?pretty -d ' { "query": { "prefix": { "name": { "value": "あいうえ" } } }, "size": 5, "from": 0 } '
結果
"hits" : { "total" : 0, "max_score" : null, "hits" : [ ] }
予想どおりヒットしないですね。