20
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Elasticsearch によるサジェスト検索実装

Last updated at Posted at 2018-04-23

はじめに

Solr によるサジェスト検索実装について書きましたが、Elasticserchで同じことをやってみました。

実験環境構築

https://github.com/ft28/practice/tree/master/es/suggestREADME.md に従って、実験環境を構築します。

実験環境説明

lucene-analyzers-kuromoji-7.2.1-SNAPSHOT.jar

前回作成した、各トークンの漢字・ひらがな・カタカナをローマ字に変換後、トークンを結合して1つのトークンを作成するモジュールを追加したjar ファイルです。

AnalysisKuromojiPlugin.java

Elasticsearch の analysis-kuromoji pluginに上記moduleを呼び出すための名前と対応するクラスを登録します。

61         extra.put("kuromoji_concat_japanese", KuromojiConcatenateJapaneseReadingFilterFactory::new);

KuromojiConcatenateJapaneseReadingFilterFactory.java

上で登録したクラスの実装ファイルです。このクラスの中で、luceneモジュールを生成し使えるようにします。

build.gradle

analysis-kuromoji pluginコンパイル時に新しく作成したlucene-analyzer-kuromoji を使うよう設定します。

build.gradle
 19 repositories {
 20   mavenLocal()
 21 }

 28 dependencies {
 29 // compile "org.apache.lucene:lucene-analyzers-kuromoji:${version.lucene}"
 30 // compile "test:lucene-analyzers-kuromoji:7.2.3"
 31    compile files("lucene-analyzers-kuromoji-7.2.1-SNAPSHOT.jar")
 32 }

実験

検索対象のフィールドword に対して、filterにローマ字読みサジェスト設定でよく使うkuromoji_readingformを使用したインデックスと、今回作成したkuromoji_concat_japaneseを使用したインデックスを作成するよう定義します。

mapping.json
        "word": {
          "type": "text",
          "analyzer": "default_index_analyzer",
          "search_analyzer": "default_search_analyzer",
          "fields": {
             "roman1": {
               "type": "text",
               "analyzer": "roman1_index_analyzer",
               "search_analyzer": "roman1_search_analyzer"
             },
             "roman2": {
               "type": "text",
               "analyzer": "roman2_index_analyzer",
               "search_analyzer": "roman2_search_analyzer"
             }
          }
        }

|作成時|roman1_index_analyzer|roman2_index_analyzer|
|---|---|---|---|
|tokenizer|kuromoji_tokenizer|kuromoji_tokenizer|
|filters|kuromoji_readingform
edgeNGram|kuromoji_concate_japanese
edgeNGram|

検索時 roman1_search_analyzer roman2_search_analyzer
tokenizer kuromoji_tokenizer kuromoji_tokenizer
filters kuromoji_readingform kuromoji_concate_japanese

インデックス作成時はedgeNGramを作り、検索時はedgeNGramを作りません

analyzer 比較

Elasiticsearchの場合、各analyzerで元データに対して、どのようなトークンが作成されるか以下手順で確認出来ます。

curl -XGET 'localhost:9200/suggest/_analyze?pretty' -d '{"analyzer": "<analyzer名>", "text": "<元データ>"}' -H 'Content-Type: application/json'

インデックス作成時に「品川」に対して各analyzerで作成されるトークンは以下のようになります。

analyzer名 roman1_index_analyzer roman2_index_analyzer
トークン1 s s
トークン2 sh sh
トークン3 shi shi
トークン4 shin shin
トークン5 shina shina
トークン6 shinaga shinaga
トークン7 shinagaw shinagaw
トークン8 shinagawa shinagawa
  • 「品川」はtokenizerから出力された時点で1つのトークンなので、romen1_index_analyzer、roman2_index_analyzerが作成するトークンは同じになります。

検索時の「しながわ」に対して各analyzer で作成されるトークンは以下のようになります。

analyzer名 roman1_search_analyzer roman2_search_analyzer
トークン1 shina shinagawa
トークン2 ga
トークン3 wa
  • 「しながわ」は、tokenizerで「しな」「が」「わ」に分解され、roman1_search_analyzerでは、ローマ字置換されたトークンがされていますが、roman2_search_analyzerでは結合して1つのトークンになっていることが分かります。

検索結果比較

  • roman1 で検索
# クエリ
curl -XGET http://localhost:9200/suggest/keyword/_search?pretty -H 'Content-Type: application/json' -d '
{
   "query": {
     "match": { 
       "word.roman1": {
          "query": "しながわ",
          "operator": "AND"
       }
     }
   }
 }'
# 結果
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 0,
    "max_score" : null,
    "hits" : [ ]
  }
}
  • roman2 で検索
# クエリ
curl -XGET http://localhost:9200/suggest/keyword/_search?pretty -H 'Content-Type: application/json' -d '
{
   "query": {
     "match": { 
       "word.roman2": {
          "query": "しながわ",
          "operator": "AND"
       }
     }
   }
 }'
# 結果
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 6.3382335,
    "hits" : [
      {
        "_index" : "suggest",
        "_type" : "keyword",
        "_id" : "279",
        "_score" : 6.3382335,
        "_source" : {
          "word" : "品川"
        }
      }
    ]
  }
}

まとめ

Elasticsearch でも Solrの時に作成したluceneモジュールを使って、サジェスト検索を改善できました。gradleによるビルドを今回はじめて触ったので、その点で少し苦労しましたが、それ以外は、Solr の時とほぼ同じ手順で組み込めました。

参考

Elasticsearch キーワードサジェスト日本語のための設計

  • Elasticsearch のサジェスト設定について参考にさせて頂きました。
  • 今回は比較対象モジュール部分に必要最小限の設定しか行っていないのですが、実用ではこちらのサイトにあるような設定を追加するのが良いと思われます。

Solr にジェスト検索実装

  • luceneモジュールはこちらで作成したものを使っています。
  • luceneモジュールの作成方法はこちらの記事をご参照ください。
20
16
0

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
20
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?