DEV Community

kaede
kaede

Posted on

Elastic Search アルファベットの大文字小文字を区別する検索と区別しない検索を実装する

why

日本語の部分一致検索をしたかった。

https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-fields.html

ES の公式ガイドを見ると

PUT indexName
{
  "mappings": {
    "properties": {
      "keyName": {
        "type": "text",
        "fields": {
          "raw": { 
            "type":  "keyword"
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

その index の mapping を PUT で更新すれば良いようだ。
fileds の raw の type を keyword にすれば
少文字大文字で区別せずに検索できるようになるらしい?


実際に自分のインデックスに打って確かめてみる

これを打つと text の raw の 中身が keyword で作られた。

{
  "error": {
    "root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "mapper [name] cannot be changed from type [text] to [keyword]"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "mapper [name] cannot be changed from type [text] to [keyword]"
  },
  "status": 400
}
Enter fullscreen mode Exit fullscreen mode

PUT だから本体の text を keyword に変更しようとしてみたが
変更できないとエラーがでた。

        "type": "illegal_argument_exception",
        "reason": "mapper [name.raw] cannot be changed from type [keyword] to [text]"
Enter fullscreen mode Exit fullscreen mode

中の raw という keyword も
PUT で text <-> keyword に変更することはできなかった。

{
  "acknowledged": true
}
Enter fullscreen mode Exit fullscreen mode

同じ値でリクエストすると「知ってるよ」ってレスポンスが来る。
よって、クエリを間違えているわけではなさそうだ。

raw について調べてみる

https://stackoverflow.com/a/38538096

city: New York の場合は
new と york のトークンが city フィールドに入っている
city.raw のフィールドには New York の完全なキーワードが入っている。
こういうことらしい。


dan に DaN でリクエストしてみる。

親である text の dan のデータにリクエストしてみる

GET /customer/_search
{
  "query": {
      "match": { "name": "DaN" } 
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

前回作った dan のデータが

    "hits": [
      {
        "_index": "customer",
        "_id": "3",
        "_score": 0.9808291,
        "_source": {
          "name": "dan"
        }
      }
    ]
Enter fullscreen mode Exit fullscreen mode

DaN の検索でヒットした。

https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html#text-search-types

既にデフォルトの text 型なら大文字小文字区別なくひっかかる?

da では引っかからない。

https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html#keyword

反対に keyword 型は

  • ID
  • HostAddress
  • Status Code
  • Zip Code

こういう形式の決まっている値に向いているっぽい。

https://qiita.com/mctk/items/0a072e758811d90d4642#elasticsearch%E3%81%A7%E3%81%AE%E5%AE%9F%E8%A3%85%E6%96%B9%E6%B3%95

keyword は単語で分割して保存されないので、match で部分一致検索できないらしい。


keyword 型で検索する

https://stackoverflow.com/questions/41722812/elasticsearch-can-i-query-the-raw-field

これを読むと、raw を検索するには re-index をしないといけないが
クエリはあっているらしい。

GET /customer/_search
{
  "query": {
      "match": { "name.keyword": "dan" } 
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
    "hits": [
      {
        "_index": "customer",
        "_id": "3",
        "_score": 0.9808291,
        "_source": {
          "name": "dan"
        }
      }
    ]
Enter fullscreen mode Exit fullscreen mode

なので、中身の keyword に dan でかけると取れて

GET /customer/_search
{
  "query": {
      "match": { "name.keyword": "dAn" } 
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
    "hits": []
Enter fullscreen mode Exit fullscreen mode

dAn でかけると取れなかった。


まとめ

Elastic Search の index に対して key value のセットを追加すると、
デフォルトでは text 型で保存される。

text 型は単語で分割して保存されるので、__search match を打つだけで
アルファベットでは、大文字小文字の曖昧検索が実装できる。

一方、大文字小文字を厳密に検索したい場合は、

GET /customer/_search
{
  "query": {
      "match": { "name.keyword": "dan" } 
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

このように keyName.keyword で打てば
厳密に検索できる。

Top comments (0)