LoginSignup
4
5

More than 3 years have passed since last update.

Elasticsearch 7.2.1 で Wildcard query を使って前方一致検索を行う際に、検索文字列に対して正規化を行う

Posted at

Elasticsearch で前方一致検索を行う際に、検索文字列に正規化を行いたい時があります。
その場合、検索を実行するアプリケーション側で正規化を行うこともできますが、Elasticsearch側で正規化を行うことも可能です。

手元の環境は以下になります。

  • Ubuntu 18.04 LTS
  • Elasticsearch 7.2.1
  • ICU Analysis Plugin

最初にインデックスの設定とマッピングを定義します。( jq コマンドで整形してます。)
Wildcard query は Term-level queries になるため、アナライザが適用されません。
そのため、Match query のようにアナライザの設定でフィルターを指定するのではなく、normalizer を直接指定する必要があります。
今回は goods データに name フィールドのみの状態を作ってみます。

$ curl -X PUT 'localhost:9200/shop' -H 'Content-Type: application/json' -d'
{
    "settings": {
        "index": {
            "number_of_shards": 2,
            "number_of_replicas": 1,
            "refresh_interval": "-1",
            "analysis": {
                "normalizer": {
                    "shop_normalizer": {
                        "type": "custom",
                        "char_filter": [
                            "icu_normalizer"
                        ]
                    }
                }
            }
        }
    },
    "mappings": {
        "properties": {
            "type": {"type": "keyword"},
            "name": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "normalizer": "shop_normalizer",
                        "ignore_above": 256
                    }
                }
            }
        }
    }
}' | jq

次に、以下のようなデータで json ファイルを作成します。
カタカナ、および半角スペースが混じったデータにしてみました。

{"index": {"_index": "shop", "_type": "_doc","_id": "goods_id.1"}}
{"type": "goods","name": "タンカンジャム"}
{"index": {"_index": "shop", "_type": "_doc","_id": "goods_id.2"}}
{"type": "goods","name": "ジェラード 屋久島"}

そして、json ファイルを使ってインデクシングします。

$ curl -H "Content-type: application/x-ndjson" -X POST http://localhost:9200/_bulk?refresh=false --data-binary @request_bulk.json | jq
$ curl -X POST 'localhost:9200/shop/_refresh' | jq

全角と半角のどちらの文字列でも検索できることを確認します。

$ curl -X POST 'localhost:9200/shop/_search' -H 'Content-Type: application/json' -d'
{
    "from": 0,
    "size": 10,
    "query": {
        "wildcard": {
            "name.keyword": {
                "value": "タンカン*"
            }
        }
    }
}
' | jq

以下のように結果が返ってきます。

{
  "took": 9,
  "timed_out": false,
  "_shards": {
    "total": 2,
    "successful": 2,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "shop",
        "_type": "_doc",
        "_id": "goods_id.1",
        "_score": 1,
        "_source": {
          "type": "goods",
          "name": "タンカンジャム"
        }
      }
    ]
  }
}
$ curl -X POST 'localhost:9200/shop/_search' -H 'Content-Type: application/json' -d'
{
    "from": 0,
    "size": 10,
    "query": {
        "wildcard": {
            "name.keyword": {
                "value": "タンカン*"
            }
        }
    }
}
' | jq

以下のように結果が返ってきます。

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 2,
    "successful": 2,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "shop",
        "_type": "_doc",
        "_id": "goods_id.1",
        "_score": 1,
        "_source": {
          "type": "goods",
          "name": "タンカンジャム"
        }
      }
    ]
  }
}

全角スペースが混じった文字列でも検索できることを確認します。

$ curl -X POST 'localhost:9200/shop/_search' -H 'Content-Type: application/json' -d'
{
    "from": 0,
    "size": 10,
    "query": {
        "wildcard": {
            "name.keyword": {
                "value": "ジェラード 屋久*"
            }
        }
    }
}
' | jq

以下のように結果が返ってきます。

{
  "took": 8,
  "timed_out": false,
  "_shards": {
    "total": 2,
    "successful": 2,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "shop",
        "_type": "_doc",
        "_id": "goods_id.2",
        "_score": 1,
        "_source": {
          "type": "goods",
          "name": "ジェラード 屋久島"
        }
      }
    ]
  }
}

Wildcard query を実行する際に rewrite パラメータで挙動を制御できるようですが、基本的にはデフォルトの値の方がパフォーマンスが良いようです。

参考になった記事

Term-level queries
Wildcard query

4
5
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
4
5