Elastic Search アルファベットの大文字小文字を区別する検索と区別しない検索を実装する
Tags: elasticsearch
Published: 2022-07-31 15:37:38




## why

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

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

ES の公式ガイドを見ると

```json
PUT indexName
{
  "mappings": {
    "properties": {
      "keyName": {
        "type": "text",
        "fields": {
          "raw": { 
            "type":  "keyword"
          }
        }
      }
    }
  }
}
```

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

---

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

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

```json
{
  "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
}
```

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

```json
        "type": "illegal_argument_exception",
        "reason": "mapper [name.raw] cannot be changed from type [keyword] to [text]"
```

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

```json
{
  "acknowledged": true
}
```

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

## raw について調べてみる

https://stackoverflow.com/a/38538096

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


---

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

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


```json
GET /customer/_search
{
  "query": {
      "match": { "name": "DaN" } 
    }
  }
}
```

前回作った dan のデータが

```json
    "hits": [
      {
        "_index": "customer",
        "_id": "3",
        "_score": 0.9808291,
        "_source": {
          "name": "dan"
        }
      }
    ]
```

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 をしないといけないが
クエリはあっているらしい。

```json
GET /customer/_search
{
  "query": {
      "match": { "name.keyword": "dan" } 
    }
  }
}
```

```json
    "hits": [
      {
        "_index": "customer",
        "_id": "3",
        "_score": 0.9808291,
        "_source": {
          "name": "dan"
        }
      }
    ]
```

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

```json
GET /customer/_search
{
  "query": {
      "match": { "name.keyword": "dAn" } 
    }
  }
}
```

```json
    "hits": []
```

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

---

## まとめ

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

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

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

```json
GET /customer/_search
{
  "query": {
      "match": { "name.keyword": "dan" } 
    }
  }
}
```

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