ElasticSearch Analyerについて

# アナライザーの構成

- Character filters
- Tokenizer （必須）
- Token filters

## アナライザーの処理フロー
```plaintext
<html><body>Quick Brown Fox!</body></html>

↓ char_filter => html_strip

Quick Brown Fox!

↓ tokenizer => whitespace

Quick
Brown
Fox!

↓ filter => lowercase

quick
brown
fox!
```


## Character filters
文字列に対し、Tokenizerで分割する前に必要な処理（追加、削除、変更）を行う。
```ruby
'ngram_analyzer': {
   tokenizer: 'ngram_tokenizer',
   filter: ['kana_filter'],
   char_filter: [:icu_normalizer] # <- 🌟
}
```

### icu_normalizer
Unicodeを正規化するもの
例
```
GET _analyze
{
  "text": "㈱Ｌｉｎｋｏｄｅはｿﾌﾄｳｪｱの開発をしています。",
  "char_filter": ["icu_normalizer"]
}

# result
{
  "tokens" : [
    {
      "token" : "(株)linkodeはソフトウェアの開発をしています。",
      "start_offset" : 0,
      "end_offset" : 25,
      "type" : "word",
      "position" : 0
    }
  ]
}
```
㈱が(株)に展開され、アルファベットは半角小文字に統一、半角カタカナは全角になっている

## Tokenizer
文字列を単語レベルに分割する役割を持つ。
N-gram とか指定してする（？）

## Token filters
Tokenizerで分割された内容に対し、必要な処理（追加、削除、変更）を行う。

### icu_transform
大文字と小文字のマッピング、正規化、音訳、双方向テキスト処理など、さまざまな方法でUnicodeテキストを処理するために使用される。
id を指定することでどの変換を行うかを指定する。

id: Katakana-Hiragana -> カタカタをひらがなに変換


## 例

```ruby
analyzer: {
  'ngram_analyzer': {
    tokenizer: 'ngram_tokenizer',
    filter: ['kana_filter'],
    char_filter: [:icu_normalizer]  # Character filter
  },
}

# Token filter
'kana_filter': {
  type: :icu_transform,
  id: 'Katakana-Hiragana'
}

# Tokenizer
'ngram_tokenizer': {
  type: :ngram,
  min_gram: 1,
  max_gram: 4,
   # 対象とする文字列の種類
   # symbol が設定されていないので、記号は弾かれる
   # https://christina04.hatenablog.com/entry/2015/02/02/225734
  token_chars: %i(letter digit) 
}
```
```
curl -H "Content-Type: application/json" -XGET 'localhost:9200/<index>/_analyze' -d '
{ "analyzer": "ngram_analyzer",
  "text" : "nttデータ"
}'
```

「nttデータ」が `kana_filter` によって、 「データ」が「でえた」に変換されていることがわかる。
```
{
  "tokens": [
    {
      "token": "n",
      "start_offset": 0,
      "end_offset": 1,
      "type": "word",
      "position": 0
    },
    {
      "token": "nt",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 1
    },
    {
      "token": "ntt",
      "start_offset": 0,
      "end_offset": 3,
      "type": "word",
      "position": 2
    },
    {
      "token": "nttで",
      "start_offset": 0,
      "end_offset": 4,
      "type": "word",
      "position": 3
    },
    {
      "token": "t",
      "start_offset": 1,
      "end_offset": 2,
      "type": "word",
      "position": 4
    },
    {
      "token": "tt",
      "start_offset": 1,
      "end_offset": 3,
      "type": "word",
      "position": 5
    },
    {
      "token": "ttで",
      "start_offset": 1,
      "end_offset": 4,
      "type": "word",
      "position": 6
    },
    {
      "token": "ttでえ",
      "start_offset": 1,
      "end_offset": 5,
      "type": "word",
      "position": 7
    },
    {
      "token": "t",
      "start_offset": 2,
      "end_offset": 3,
      "type": "word",
      "position": 8
    },
    {
      "token": "tで",
      "start_offset": 2,
      "end_offset": 4,
      "type": "word",
      "position": 9
    },
    {
      "token": "tでえ",
      "start_offset": 2,
      "end_offset": 5,
      "type": "word",
      "position": 10
    },
    {
      "token": "tでえた",
      "start_offset": 2,
      "end_offset": 6,
      "type": "word",
      "position": 11
    },
    {
      "token": "で",
      "start_offset": 3,
      "end_offset": 4,
      "type": "word",
      "position": 12
    },
    {
      "token": "でえ",
      "start_offset": 3,
      "end_offset": 5,
      "type": "word",
      "position": 13
    },
    {
      "token": "でえた",
      "start_offset": 3,
      "end_offset": 6,
      "type": "word",
      "position": 14
    },
    {
      "token": "ー",
      "start_offset": 4,
      "end_offset": 5,
      "type": "word",
      "position": 15
    },
    {
      "token": "ーた",
      "start_offset": 4,
      "end_offset": 6,
      "type": "word",
      "position": 16
    },
    {
      "token": "た",
      "start_offset": 5,
      "end_offset": 6,
      "type": "word",
      "position": 17
    }
  ]
}
```

# 参考記事
https://blog.linkode.co.jp/entry/2020/07/20/150701