LoginSignup
233
227

More than 5 years have passed since last update.

Kibana+Elasticsearchで文字列の完全一致と部分一致検索の両方を実現する

Posted at

Elasticsearchはデフォルトで文字列を要素解析して保存する。これによって部分一致検索ができるようになるのだが、完全一致検索が難しくなる。

便利なTermsがうまくいかない

KibanaにはTermsという値の上位10件とかを表示してくれるパネルがある。次のようなデータを入れているとする。

example.json
{
  "@timestamp": "2013-12-17T10:12:40+09:00",
  "remote_addr": "203.0.113.10",
  "country_code": "JP",
  "request_uri": "/index.php",
  "user_agent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"
}

すると、いまアクセスされている国の比率とかを見たければ、country_codeを指定してあげれば次の様なグラフが書けて非常に便利だ。

terms of country

しかし、user_agentを指定するとつぎのようになったりして、うまくいかないことがある。

user_agent fault

これはKibanaが空気を読んで要素解析してしまったせいである。できれば完全一致で見たい。

Mapping Templateを使う

Elasticsearchはスキーマレスでインデックスが増えたら勝手にデータ型とかを判別してくれるが、フィールドのデータ型や、要素解析などを指定することもできる。そのためにはMapping Templateを使う。

test_template.json
{
    "template": "*",
    "mappings": {
        "_default_": {
            "_source": { "compress": true },
            "properties" : {
                "request_uri" : { "type" : "string", "index" : "not_analyzed" }
            }
        }
    }
}

templateフィールドで、どのインデックスにマッチするかを指定する。ここでは"*"としているのですべてのインデックスにマッチする。例えば"access-*"とすれば、access-から始まるインデックスだけに適用することもできる。この例ではrequest_uriフィールドの型をstring型、インデックスの要素解析をnot_analyzedで無効にしている。これによって、request_uriの完全一致ができるようになる。

書けたらテンプレートをElasticsearchに登録する。

$ curl -XPUT elasticsearch.local:9200/test_template/ -d "`cat test_template.json `"

Multi Fieldを使う

前の例では、request_uriの要素解析をnot_analyzedにできたが、逆に部分一致検索をしようとしたらできなくなってしまった。これは困るので部分一致検索も完全一致検索も両方実現したい。そこで、1つのフィールドからデータ型や要素解析の方法が異なる複数のフィールドを生成するMulti Fieldを使う。

multi_field_template.json
{
    "template": "*",
    "mappings": {
        "_default_": {
            "_source": { "compress": true },
            "properties" : {
                "request_uri" : {
                    "type": "multi_field",
                    "fields": {
                        "request_uri": {
                            "type": "string",
                            "index" : "analyzed"
                        },
                        "full": {
                            "type": "string",
                            "index" : "not_analyzed"
                        }
                    }
                }
            }
        }
    }
}

この例では、Multi Fieldではフィールド名を指定すると、request_uri.fullの用に入れ子になったフィールドができる。また、Multi Fieldのフィールド名を元のフィールド名と同じにしておくと、request_uriで指定できるようになる。これで部分一致検索には元のrequest_uriフィールドを使い、完全一致検索はrequest_uri.fullフィールドを使って実現できるようになった。

Dynamic Templateを使う

Mapping Templateには特定のフィールドを指定しなくても、フィールド名の部分一致や型一致でテンプレートを適用してくれる。これをDynamic Templateという。このDynamic Templateを使って、すべての文字列型フィールドに先ほどのMulti Fieldを適用する。

template_all.json
{
    "template": "*",
    "mappings": {
        "_default_": {
            "_source": { "compress": true },
            "dynamic_templates": [
                {
                    "string_template" : {
                        "match" : "*",
                        "mapping": {
                            "type": "multi_field",
                            "fields": {
                                "{name}": {
                                    "type": "string",
                                    "index" : "analyzed"
                                },
                                "full": {
                                    "type": "string",
                                    "index" : "not_analyzed"
                                }
                            }
                        },
                        "match_mapping_type" : "string"
                    }
                }
            ],
            "properties" : {
                "@timestamp" : { "type" : "date", "index" : "not_analyzed" }
            }
        }
    }
}

Termsがうまくいくようになった

こんなかんじでできるようになって便利だ。

131217-0004.png

Mapping Templateを追加したが適用されないとき

Mapping Templateは新しくインデックスができた時に適用されるので、インデックスを作り直すか、新しいインデックスを作るかしないと適用されない。fluent-plugin-elasitcsearchからログを入れているのであれば、日本時間9:00 AMになれば新しくインデックスができて適用されるはずだ。

まとめ

Mapping Templateを使うと特定フィールドのデータ型を指定したりできてとても便利だ。しかし、自分はあまりこの機能を使わないようにしている。なぜならElasticsearchが持つスキーマレスなデータ型というのが失われるからで、できるだけデータを挿入するときにデータ型には気を遣うべきだと思っている。

とはいえ文字列の完全一致は使いたい。なので、文字列はnot_analyzedにしてしまうか、Multi Fieldをうまく使ってよくするのが良いと思う。

233
227
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
233
227