ElasticsearchでCentOS上にNgram全文検索サーバを構築する - (その2)Ngramアナライザを設定する
前回に引き続きElasticsearchの設定を行います。
elasticsearch-headプラグインをインストールする
いろいろ設定を試していたりすると、設定内容やデータの確認のためにコンソールからcurlを実行してREST APIを実行するのが面倒になります。そこでElasticsearchにはWebUIからデータや設定内容を参照するための機能がプラグインで用意されているので、これを導入します。
インストールは以下のコマンドを実行するだけでOKです。
$ sudo /usr/share/elasticsearch/bin/plugin --install mobz/elasticsearch-head
あとはブラウザから
http://localhost:9200/_plugin/head/
にアクセスすればWebUIからデータの操作ができるようになります。
アナライザ
アナライザは、文字列タイプのフィールドをElasticsearchにインデックスする(データを保存する)ときや検索クエリを投げるときに行われる処理で、データを品詞分解したり、大文字小文字の入力を変換したりするテキストの解析処理のことです。インデックスするときと検索時に別々のアナライザを使うこともできます。
デフォルトでいくつかアナライザが用意されていますが、自分で定義することもできます。 今回は日本語をNgram検索をしたいのでカスタムアナライザを定義しました。なおNgramについてはググるか
http://gihyo.jp/dev/serial/01/make-findspot/0005
などの参照してください。
/etc/elasticsearch/elasticsearch.yml
に以下の定義を追加します
# default analyzer (1-gram and 2-gram)
index.analysis.analyzer.default.tokenizer: custom_ngram_tokenizer
index.analysis.analyzer.default.filter.0: lowercase
index.analysis.tokenizer.custom_ngram_tokenizer.type: nGram
index.analysis.tokenizer.custom_ngram_tokenizer.min_gram: 1
index.analysis.tokenizer.custom_ngram_tokenizer.max_gram: 2
index.analysis.tokenizer.custom_ngram_tokenizer.token_chars.0: letter
index.analysis.tokenizer.custom_ngram_tokenizer.token_chars.1: digit
# default_search analayzer(2-gram)
index.analysis.analyzer.default_search.tokenizer: custom_bigram_tokenizer
index.analysis.analyzer.default_search.filter.0: lowercase
index.analysis.tokenizer.custom_bigram_tokenizer.type: nGram
index.analysis.tokenizer.custom_bigram_tokenizer.min_gram: 2
index.analysis.tokenizer.custom_bigram_tokenizer.max_gram: 2
index.analysis.tokenizer.custom_bigram_tokenizer.token_chars.0: letter
index.analysis.tokenizer.custom_bigram_tokenizer.token_chars.1: digit
設定を反映させるため、Elasticsearchを再起動し、データを再投入します。
さて、上記のアナライザの定義ですが、2つのカスタムアナライザを定義しています。まず最初の
# default analyzer (1-gram and 2-gram)
index.analysis.analyzer.default.tokenizer: custom_ngram_tokenizer
index.analysis.analyzer.default.filter.0: lowercase
部分ですが、index.analysis.analyzer.default
というキーでデフォルトのアナライザを定義しています。index.analysis.analyzer.default.tokenizer: custom_ngram_tokenizer
はトークナイザ(品詞分解する処理)にcustom_ngram_tokenizer
を使うことを設定しています。で、このcustom_ngram_tokenizer
はその下に設定内容が書いてあります。
index.analysis.tokenizer.custom_ngram_tokenizer.type: nGram
index.analysis.tokenizer.custom_ngram_tokenizer.min_gram: 1
index.analysis.tokenizer.custom_ngram_tokenizer.max_gram: 2
index.analysis.tokenizer.custom_ngram_tokenizer.token_chars.0: letter
index.analysis.tokenizer.custom_ngram_tokenizer.token_chars.1: digit
文字を1-gramおよび2-gramで分解する設定となっています。つまり、
こんにちは
という文字は
こん, んに, にち, ちは
こ,ん,に,ち,は
というように分解されインデックスされます。さて、もう一つのアナライザですが、
# default_search analayzer(2-gram)
index.analysis.analyzer.default_search.tokenizer: custom_bigram_tokenizer
index.analysis.analyzer.default_search.filter.0: lowercase
という定義になっています。このindex.analysis.analyzer.default_search
という設定は、検索時のみに使われるアナライザのデフォルトになります。こちらのトークナイザの設定はcustom_bigram_tokenizer
で、これは
index.analysis.tokenizer.custom_bigram_tokenizer.type: nGram
index.analysis.tokenizer.custom_bigram_tokenizer.min_gram: 2
index.analysis.tokenizer.custom_bigram_tokenizer.max_gram: 2
index.analysis.tokenizer.custom_bigram_tokenizer.token_chars.0: letter
index.analysis.tokenizer.custom_bigram_tokenizer.token_chars.1: digit
となっており、2-gramで分解する設定です。こんにちは
の例ですと
こん, んに, にち, ちは
という分解を行います。 で、なんでインデックス時と検索時のアナライザを別々に定義しているのかというと、1文字による検索でも何らかの検索結果を返したいと考えて設計したからです。
2-gramのみでインデックスしてしまうと、1文字による検索に一切マッチしません。 一方、検索時は2-gramのみをおこなっていますが、このアナライザに1文字の検索クエリをなげると、
# 以下はアナライザの動作確認をおこなうリクエスト
$ curl -XGET 'http://localhost:9200/blog/_analyze?analyzer=default_search&pretty=true' -d 'a'
{
"tokens" : [ ]
}
# 検索
$ curl -XGET http://localhost:9200/blog/article/_search?pretty=true -d '{"query": {"match":{"_all":"a"}}}'
{
"took" : 10,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : null,
"hits" : [ ]
}
のように、トークンが空になってしまい、そのままですとやはり検索に引っかかりません。 そこで一文字の時は、検索にアナライザを利用しないようにqueryをmatchからtermに変更して、以下のようなリクエストをなげるようにしました。
$ curl -XGET http://localhost:9200/blog/article/_search?pretty=true -d '{"query": {"term":{"_all":"a"}}}'
これで一文字のときも検索に引っかかるようになります。
マッピング
今回はまず導入ということで、フィールドに対してマッピングは行いませんでした。 マッピングは個々のフィールドに詳細な設定ができる、RDBMSでいうところのスキーマ定義に当たります。 マッピングを使えば特定のフィールド(たとえばブログのタイトル部分だけ)に特定のアナライザを適用したりとか、もっと高度で効率的な検索もできると思います。 しかし上記のデフォルトアナライザの設定だけでも、ドキュメントのテキスト部分全体に対してNgram検索がかけられて、いい感じの検索結果を得ることができています。