Yoshi.dev

技術系の趣味、またはやった仕事やそこから学んだことを忘れないために

ElasticSearchでの類似キーワード検索

ElasticSearchをここ数日使っていて、レーベンシュタイン距離による類似キーワードの検索ができないか試していた

下記のような銀行口座の明細テキストを扱うデータでの類似度による検索をやりたかった

東京電力  料金   17/04
東京電力  料金   17/06
東京電力  料金   17/07
東京電力  料金   17/08
東京電力  料金   17/09
東京電力  料金   17/10
東京電力  料金   17/11
東京電力  料金   17/12
東京電力  料金   18/01
東京電力  料金   18/02

例えば「東京電力  料金   17/12」というキーワードで検索したら、上記のテキストがすべて返ってくるようにしたい

サンプルデータ登録

PUT /whitespace/doc/_bulk
{"index": {"_id": 1}}
{"analyzer": "whitespace","text": "東京電力  料金   17/11"}
{"index": {"_id": 2}}
{"analyzer": "whitespace","text": "au ご利用代金 12月ご利用分"}
{"index": {"_id": 3}}
{"analyzer": "whitespace", "text": "au ご利用代金 1月ご利用分"}
{"index": {"_id": 4}}
{"analyzer": "whitespace", "text": "au ご利用代金 10月ご利用分"}
{"index": {"_id": 5}}
{"analyzer": "whitespace", "text": "au ご利用代金 11月ご利用分"}
{"index": {"_id": 6}}
{"analyzer": "whitespace", "text": "au ご利用代金 12月ご利用分"}
{"index": {"_id": 7}}
{"analyzer": "whitespace", "text": "au ご利用代金 2月ご利用分"}
{"index": {"_id": 8}}
{"analyzer": "whitespace", "text": "au ご利用代金 3月ご利用分"}
{"index": {"_id": 9}}
{"analyzer": "whitespace", "text": "VISA国内利用 VS ニユ-ズピツクス"}
{"index": {"_id": 12}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/03"}
{"index": {"_id": 13}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/04"}
{"index": {"_id": 14}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/06"}
{"index": {"_id": 15}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/07"}
{"index": {"_id": 16}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/08"}
{"index": {"_id": 17}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/09"}
{"index": {"_id": 18}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/10"}
{"index": {"_id": 19}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/03"}
{"index": {"_id": 20}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/04"}
{"index": {"_id": 21}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/06"}
{"index": {"_id": 22}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/07"}
{"index": {"_id": 23}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/08"}
{"index": {"_id": 24}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/09"}
{"index": {"_id": 25}}
{"analyzer": "whitespace", "text": "東京電力  料金   17/10"}

analyzerに関しては、明細テキストの場合普通の日本語文章とは異なるので、よく日本語を扱うときに使用されるkuromojiは使わなかった。 代わりにスペースで区切ればいいだろうと思ったので"whitespace"を使用した

検索クエリは下記のような感じ

GET whitespace/_search
{
  "from" : 0, "size" : 20,
  "query": {
    "fuzzy": {
      "text.keyword": {
        "value": "東京電力  料金   17/10"
      }
    }
  }
}

このようにfuzzy query検索をすると、レーベンシュタイン距離による類似度検索が行える いくつかパラメータがあるようだけど内容は

Fuzzy Query | Elasticsearch Reference [6.3] | Elastic

公式を参照

自分がハマったのだが、textというフィールドを対象にするとき .keyword を付けないとうまくいかなかった。なぜだかはまだわかっていないけど、mappingを確認するとtype: keywordになっていたので、type: keywordのときは明示的に書かないといけないのかな。