ElasticSearch テンプレートでインデックスの型を指定する
ElasticSearchにデータを突っ込んで、文字列の完全一致の件数を集計したいことがあった。
ElasticSearchは、自動で型変換する全文検索エンジンなので特に型指定をしないと、下記のようにトークナイザによって要素分割されて格納される。
今回のケースでは、下記2つの文字列が格納されたレコードがある場合に、warnと完全一致するレコードの件数をカウントする為の方法を書こうと思う。
例)
①warn 123
②warn
ElasticSearchのクエリで、warnの文字列マッチングでLuceneクエリを書くと、
①+②が両方がカウントされて件数が出てきてしまう。
これは、ElasticSearch上で要素分解されて、②は[warn]+[123]のように格納される為、warnでの部分一致で上記検索にマッチしてしまう為である。
では、どうするか。
ElasticSearchにはテンプレートという機能があり、これを使えば要素分解せずに値を素のままで格納するような型指定[not_analyzed]を出来るのでこれを使う。
下記Qiitaの記事とjohtaniさんのElasticSearch入門を非常に参考にさせていただきました。ありがとうございます。
http://qiita.com/harukasan/items/4ec517d8d96f557367e1
http://blog.johtani.info/images/entries/20130830/IntroductionES20130829.pdf
任意の場所に下記のようなJSONを配置
※下記のテンプレートの意味は、
1.messageを要素分析された形とされない形で格納("type":"multi_field")
2.tagを要素分析されない形のみにするようなテンプレート
<syslog_template.json>
{
"template":"syslog-*", //対象となるINDEX
"mappings":{
"_default_":{
"_source":{"compress":true},
"properties":{
"message":{
"type":"multi_field", //1の部分 multi_fieldの指定で一つのpropertiesに対して、複数のfieldを持たすこと可能
"fields":{
"message":{ //要素解析あり
"type":"string",
"index":"analyzed"
},
"full":{ //要素解析なし
"type":"string",
"index":"not_analyzed"
}
}
},
"tag":{
"type":"string",
"index":"not_analyzed"
}
}
}
}
}
}
上記テンプレートを作ったら、下記リクエストをESに投げてテンプレートを設置
# curl -XPUT localhost:9200/_template/syslog_template -d "`cat syslog_template.json`"
INDEX作成時しかテンプレートは読まれないようなので、既に出来ているインデックスに対しては、有効にならない。
今回の例で言うと、syslog-??というインデックスを新規作成するとテンプレートが読み込まれ意図した型のインデックスが出来ているはず。
新規に作成し、ElasticSearchのheadでmetadata確認したところ下記のように意図した型に。
message: {
type: multi_field
fields: {
message: {
type: string
}
full: {
include_in_all: false
index: not_analyzed
omit_norms: true
index_options: docs
type: string
}
}
}
tag: {
index: not_analyzed
omit_norms: true
index_options: docs
type: string
}
後はLuceneのクエリで、message.fullに対してwarnを検索するとノイズなく完全一致のもののみ抽出出来る。
ちなみに、messageに対して同じクエリを投げると、部分一致も含んだセットが返される。これを使いわければElasticSearch+kibanaでのグラフ化の幅も広がりそうな気がした。
ElasticSearchは、文献少なくチューニングどころもまだまだわからないので引き続き研究していこうと思う。