ElasticsearchでSuggestersを日本語で試す


Suggestesを以下の4つの種類があります。

* Term Suggester: 編集距離(Edit Distance)によってサジェストする。
* Phrase Suggester
* Completion Suggester
* Context Suggester

[ElasticsearchのSuggesterで、サジェストを試してみる](http://d.hatena.ne.jp/Kazuhira/20160213/1455372283)を試しながらやっていきます。

## マッピングの設定

```json

PUT suggest_sample
{
  "settings": {
    "index": {
      "number_of_shards": 5,
      "number_of_replicas": 1,
      "analysis": {
        "tokenizer": {
          "kuromoji_tokenizer_search": {
            "type": "kuromoji_tokenizer",
            "mode": "search",
            "discard_punctuation": "true"
          }
        },
        "filter": {
          "kuromoji_no_romaji_readingform": {
            "type": "kuromoji_readingform",
            "use_romaji": false
          },
          "kana_filter": {
            "type": "icu_transform",
            "id": "Katakana-Hiragana"
          }
        },
        "analyzer": {
          "kuromoji_analyzer": {
            "type": "custom",
            "tokenizer": "kuromoji_tokenizer_search",
            "filter": [
              "kuromoji_baseform",
              "kuromoji_part_of_speech",
              "cjk_width",
              "stop",
              "ja_stop",
              "kuromoji_stemmer",
              "lowercase"
            ]
          },
          "kuromoji_reading_analyzer": {
            "type": "custom",
            "tokenizer": "kuromoji_tokenizer_search",
            "filter": [
              "kuromoji_baseform",
              "kuromoji_part_of_speech",
              "cjk_width",
              "stop",
              "ja_stop",
              "kuromoji_stemmer",
              "kuromoji_no_romaji_readingform",
              "kana_filter",
              "lowercase"
            ]
          }
        }
      }
    }
  },
  "mappings": {
    "mytype": {
      "_source": {
        "enabled": true
      },
      "_all": {
        "enabled": true
      },
      "properties": {
        "content": {
          "type": "string",
          "store": "yes",
          "index": "not_analyzed",
          "copy_to": [
            "content_ja",
            "content_completion_ja",
            "content_reading_ja",
            "content_reading_completion_ja"
          ]
        },
        "content_ja": {
          "type": "string",
          "store": "yes",
          "index": "analyzed",
          "analyzer": "kuromoji_analyzer"
        },
        "content_completion_ja": {
          "type": "completion",
          "analyzer": "kuromoji_analyzer"
        },
        "content_reading_ja": {
          "type": "string",
          "store": "yes",
          "index": "analyzed",
          "analyzer": "kuromoji_reading_analyzer",
          "search_analyzer": "simple"
        },
        "content_reading_completion_ja": {
          "type": "completion",
          "analyzer": "kuromoji_reading_analyzer",
          "search_analyzer": "simple"
        }
      }
    }
  }
}
```

## データの登録

```json
PUT suggest_sample/mytype/1
{
  "content": "吾輩は猫である"
}

PUT suggest_sample/mytype/2
{
  "content": "我が名は青春のエッセイドラゴン"
}

PUT suggest_sample/mytype/3
{
  "content": "下町ロケット"
}

PUT suggest_sample/mytype/4
{
  "content": "北斗の拳"
}

PUT suggest_sample/mytype/5
{
  "content": "進撃の巨人"
}
```

## Term Suggester

Term Suggesterは編集距離（レーベンシュタイン距離)によるSuggesterです。レーベンシュタイン距離とは、文字列の類似度を「二つの文字列が最短で何回の編集で同じになるか」で表したものになります。編集とは削除・追加・変更です。

オプションのsizeはSuggestをいくつ取得するか指定します。

たとえば、「ドラコン」と少々間違った単語をSuggestすると以下になります。ただ、これだと、元のタイトルがでてこないので、もとのタイトルを含めたものをsuggestすることはできません。

なお、ひらがなでもカタカナでも関係なくsuggestしたいときは、content_reading_jaのfieldを使うようです。

```json
# クエリ
GET suggest_sample/_suggest
{
  "my-suggestion-1": {
    "text": "ドラコン",
    "term": {
      "field": "content_ja",
      "size": 3
    }
  }
}

# 結果
{
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "my-suggestion-1": [
    {
      "text": "ドラコン",
      "offset": 0,
      "length": 4,
      "options": [
        {
          "text": "ドラゴン",
          "score": 0.75,
          "freq": 1
        }
      ]
    }
  ]
}
```

# Phrase Suggester

Phrase Suggesterはトークンベースで、代替になりえるトークンを取得します。
こちらうまくクエリをいれてもうまくかえってこなかったので割愛

```json
# クエリ
POST suggest_sample/_search
{
  "size": 0,
  "suggest": {
    "my-suggest-1": {
      "text": "吾輩は犬である",
      "phrase": {
        "field": "content",
        "size": 10
      }
    }
  }
}
```

# Completion Suggester

prefix suggesterでよばれるもので言葉を補完するものです。
content_reading_completion_jaにしたときはひらがなで検索したときに正しい補完がでるのですが、漢字を入れたときには正しく動きません。これはsearch_analyzerをsimpleにしているからかなと思います。

```json
# クエリ
POST suggest_sample/_suggest
{
  "my-suggest-1": {
    "text": "我",
    "completion": {
      "field": "content_completion_ja"
    }
  }
}

# 結果
{
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "my-suggest-1": [
    {
      "text": "我",
      "offset": 0,
      "length": 1,
      "options": [
        {
          "text": "我が名は青春のエッセイドラゴン",
          "score": 1
        }
      ]
    }
  ]
}
```

```json
# クエリ
POST suggest_sample/_suggest
{
  "my-suggest-1": {
    "text": "わが",
    "completion": {
      "field": "content_reading_completion_ja"
    }
  }
}

# 結果
{
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "my-suggest-1": [
    {
      "text": "わが",
      "offset": 0,
      "length": 2,
      "options": [
        {
          "text": "吾輩は猫である",
          "score": 1
        },
        {
          "text": "我が名は青春のエッセイドラゴン",
          "score": 1
        }
      ]
    }
  ]
}
```

# Context Suggester

省略


# 参考
* https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html
* http://d.hatena.ne.jp/Kazuhira/20160213/1455372283