shnagaiの日記

主にエンジニアリング関連のトピックについて雑多に書いています

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は、文献少なくチューニングどころもまだまだわからないので引き続き研究していこうと思う。