読者です 読者をやめる 読者になる 読者になる

Carpe Diem

備忘録。https://github.com/jun06t

ElasticSearchのアナライザの設定

Elasticsearch

概要

ElasticSearchでアナライザを設定する方法です。
N-gram形態素解析などありますが、今回は単に設定する方法だけを紹介します。設定方法は主に以下の3通りがあります。

  • configで設定する
  • インデクス全体に設定する
  • 各フィールド個別に設定する

これらを順に説明していきます。

環境

  • Ubuntu 14.04
  • ElasticSearch 1.4.2

事前知識

ElasticSearchは機能が多い分パラメータも非常に多いです。
設定をする上で知らないと「このパラメータはどういう意味?」ということがあるので、
基本的なものだけあらかじめ紹介します。

デフォルトアナライザ

マッピング定義等で使用されるアナライザが定義されなかった場合はデフォルトのアナライザが使用されます。

設定 説明
default インデクス時と検索APIの両方でデフォルトのアナライザとして使用
default_index インデクス時のみのデフォルトのアナライザとして使用
default_search 検索時のみのデフォルトのアナライザとして使用

これを設定するとデフォルトのアナライザがその設定通りになります。

対象トークン

対象とする文字列の種類です。token_charsで設定します。

設定 説明
letter 文字
digit 数字
symbol 記号
punctuation 句読点、濁点など

では以降アナライザの設定の説明です。

configで設定する

アナライザを指定せずにインデクスを作成した場合にこの設定がデフォルトとなります。
今回はバイグラムでアナライザを設定します。

設定

/etc/elasticsearch/elasticsearch.ymlを編集します。
以下の設定を追加

# default analayzer(2-gram)
index.analysis.analyzer.default.tokenizer: custom_bigram_tokenizer

index.analysis.tokenizer.custom_bigram_tokenizer.type: nGram
index.analysis.tokenizer.custom_bigram_tokenizer.min_gram: 2
index.analysis.tokenizer.custom_bigram_tokenizer.max_gram: 2
index.analysis.tokenizer.custom_bigram_tokenizer.token_chars.0: letter
index.analysis.tokenizer.custom_bigram_tokenizer.token_chars.1: digit
index.analysis.tokenizer.custom_bigram_tokenizer.token_chars.2: symbol

設定を反映させるため、Elasticsearchを再起動します。

$ sudo service elasticsearch restart

動作確認

インデクスを作成します。

curl -XPUT 'localhost:9200/test'

アナライザの動作を確かめてみましょう。こんにちは世界という文字列を送ってみます。

$ curl 'localhost:9200/test/_analyze?pretty' -d 'こんにちは世界'
{
  "tokens" : [ {
    "token" : "こん",
    "start_offset" : 0,
    "end_offset" : 2,
    "type" : "word",
    "position" : 1
  }, {
    "token" : "んに",
    "start_offset" : 1,
    "end_offset" : 3,
    "type" : "word",
    "position" : 2
  }, {
    "token" : "にち",
    "start_offset" : 2,
    "end_offset" : 4,
    "type" : "word",
    "position" : 3
  }, {
    "token" : "ちは",
    "start_offset" : 3,
    "end_offset" : 5,
    "type" : "word",
    "position" : 4
  }, {
    "token" : "は世",
    "start_offset" : 4,
    "end_offset" : 6,
    "type" : "word",
    "position" : 5
  }, {
    "token" : "世界",
    "start_offset" : 5,
    "end_offset" : 7,
    "type" : "word",
    "position" : 6
  } ]
}

設定したとおり、バイグラムで解析されました。

インデクス全体に設定する

今度はあるインデクスtestに対して設定してみます。
トリグラムで設定してます。

設定

$ curl -XPUT 'localhost:9200/test' -d '
{
    "settings": {
        "analysis": {
            "analyzer": {
                "default": {
                    "tokenizer": "custom_trigram_tokenizer"
                }
            },
            "tokenizer": {
                "custom_trigram_tokenizer": {
                    "type": "nGram",
                    "min_gram": "3",
                    "max_gram": "3",
                    "token_chars": [
                        "letter",
                        "digit",
                        "symbol"
                    ]
                }
            }
        }
    }
}
'

動作確認

$ curl 'localhost:9200/test/_analyze?pretty' -d 'こんにちは世界'
{
  "tokens" : [ {
    "token" : "こんに",
    "start_offset" : 0,
    "end_offset" : 3,
    "type" : "word",
    "position" : 1
  }, {
    "token" : "んにち",
    "start_offset" : 1,
    "end_offset" : 4,
    "type" : "word",
    "position" : 2
  }, {
    "token" : "にちは",
    "start_offset" : 2,
    "end_offset" : 5,
    "type" : "word",
    "position" : 3
  }, {
    "token" : "ちは世",
    "start_offset" : 3,
    "end_offset" : 6,
    "type" : "word",
    "position" : 4
  }, {
    "token" : "は世界",
    "start_offset" : 4,
    "end_offset" : 7,
    "type" : "word",
    "position" : 5
  } ]
}

解析するとちゃんと3文字ずつになりました。

マッピングで設定する

個々のフィールドに対してアナライザを指定することもできます。
以下の様なデータを投入するとします。

{
  "title": "こんにちは世界",
  "content": "はじめまして世界"
}

設定

マッピングではデフォルトアナライザの指定を以下の名前でフィールドに付けます。

設定 説明
analyzer インデクス時と検索APIの両方でデフォルトのアナライザとして使用
index_analyzer インデクス時のみのデフォルトのアナライザとして使用
search_analyzer 検索時のみのデフォルトのアナライザとして使用

ではマッピングを行います。設定が大きいのであらかじめ以下の内容のmapping.jsonというファイルを作ります。
titleをバイグラムで、contentをトリグラムで解析する設定です。

{
    "settings": {
        "analysis": {
            "analyzer": {
                "bigram_analyzer": {
                    "tokenizer": "bigram_tokenizer"
                },
                "trigram_analyzer": {
                    "tokenizer": "trigram_tokenizer"
                }
            },
            "tokenizer": {
                "bigram_tokenizer": {
                    "type": "nGram",
                    "min_gram": "2",
                    "max_gram": "2",
                    "token_chars": [
                        "letter",
                        "digit"
                    ]
                },
                "trigram_tokenizer": {
                    "type": "nGram",
                    "min_gram": "3",
                    "max_gram": "3",
                    "token_chars": [
                        "letter",
                        "digit"
                    ]
                }
            }
        }
    },
    "mappings": {
        "blog": {
            "dynamic": false,
            "properties": {
                "title": {
                    "type": "string",
                    "index": "analyzed",
                    "analyzer": "bigram_analyzer"
                },
                "content": {
                    "type": "string",
                    "index": "analyzed",
                    "analyzer": "trigram_analyzer"
                }
            }
        }
    }
}

インデクスを作成します。

$ curl -XPUT 'localhost:9200/test' -d @mapping.json

ちゃんと登録されたか確認します。

$ curl -XGET 'localhost:9200/test/_mapping/blog/?pretty'
{
  "test" : {
    "mappings" : {
      "blog" : {
        "dynamic" : "false",
        "properties" : {
          "content" : {
            "type" : "string",
            "analyzer" : "trigram_analyzer"
          },
          "title" : {
            "type" : "string",
            "analyzer" : "bigram_analyzer"
          }
        }
      }
    }
  }
}

大丈夫そうですね。

動作確認

実際にデータを投入してみます。

$ curl -XPUT 'localhost:9200/test/blog/1' -d '
{
  "title": "こんにちは世界",
  "content": "はじめまして世界"
}
'

ちゃんとtitleがバイグラムで、contentがトリグラムで引っかかるか確かめます。

curl -XGET 'localhost:9200/test/_search?pretty' -d '
{
  "query": {
    "simple_query_string": {
      "fields": ["title"],
      "query": "こん"
    }
  }
}
'

以下の結果が出力されます。

{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.11506981,
    "hits" : [ {
      "_index" : "test",
      "_type" : "blog",
      "_id" : "1",
      "_score" : 0.11506981,
      "_source":
{
  "title": "こんにちは世界",
  "content": "はじめまして世界"
}

    } ]
  }
}

ではトリグラムで作ったインデクスに対して、バイグラムで検索をかけてみるとどうなるでしょう。

curl -XGET 'localhost:9200/test/_search?pretty' -d '
{
  "query": {
    "simple_query_string": {
      "fields": ["content"],
      "query": "はじ"
    }
  }
}
'

結果は以下のように引っかかりません。インデクス通りですね。

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 0,
    "max_score" : null,
    "hits" : [ ]
  }
}

トリグラムに対して3文字以上で検索してみましょう。

curl -XGET 'localhost:9200/test/_search?pretty' -d '
{
  "query": {
    "simple_query_string": {
      "fields": ["content"],
      "query": "て世界"
    }
  }
}
'

予想通りちゃんと引っかかりました。

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.11506981,
    "hits" : [ {
      "_index" : "test",
      "_type" : "blog",
      "_id" : "1",
      "_score" : 0.11506981,
      "_source":
{
  "title": "こんにちは世界",
  "content": "はじめまして世界"
}

    } ]
  }
}

以上です。お疲れ様でした。

ソース