RSS2.0

ElasticSearch でインデックス時と全文検索時で異なる analyzer を設定する

前回は ElasticSearch が全文検索クエリを処理する際の分かち書きのしくみについてふれました。ElasticSearch ではさまざまな種類の tokenizer を中心に analyzer を設計することができ、この analyzer が実際に文字列の単語分割を行ってくれます。

ElasticSearch ではインデックスに格納するドキュメントの内容(フィールドの値)と、全文検索クエリの検索キーワードに対して分かち書きが行われますが、この 2 つに対して異なる analyzer を適用することができます。今回は、インデックスにドキュメントを格納する際の転値インデックス作成時と、検索キーワードを渡した全文検索クエリでの検索時とで、異なる analyzer を利用してみたいと思います。

インデックス時と全文検索時の analyzer の単語分割結果を確認する

ElasticSearch では、インデックスにドキュメントを格納する際の転値インデックス作成時に適応される analyzer と、全文検索クエリで渡される検索キーワードに適用される analyzer を設定できます。となるとインデックス時と全文検索時に動作している analyzer の処理結果をそれぞれ確認したいということになると思います。まずはじめにこれらの分かち書きの結果を確認する方法についてみておきます。

以下のインデックスを材料に、まずは 2 種類の analyzer の動作確認をしてみます。
$ curl -XPUT http://localhost:9200/analyzer-sample01 -H "Content-type: application/json" -d '{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_whitespace_analyzer": {
          "tokenizer": "whitespace"
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "my_field01": {
          "type": "text",
          "analyzer": "my_whitespace_analyzer"
        }
      }
    }
  }
}'

インデックス時の analyzer の単語分割結果を確認する

インデックスにドキュメントを格納する際の転値インデックス作成時に適応される analyzer が、実際にどのように分かち書きを行うか確認してみます。

インデックス時の analyzer の動作は、前回も利用した Analyze API から確認できます。/{インデックス名}/_analyze エンドポイントに、field 値にフィールドの名前を、text 値にフィールドに格納する値を指定した JSON を POST します。そうすると、そのフィールドにそのフィールド値を格納した場合に analyzer が行う単語分割の結果が返されてきます。
この時、存在しないフィールドを指定してしまうと、Standard Tokenizer が設定されたデフォルトの analyzer によって分かち書きされてしまうので注意してください。
$ curl -XPOST http://localhost:9200/analyzer-sample01/_analyze -H "Content-type: application/json" -d '{
  "field": "my_field01",
  "text": "ひさかたの 光のどけき 春の日に"
}'
my_field01 フィールドには、Whitespace Tokenizer の設定された analyzer が指定されているため、このフィールドに値『ひさかたの 光のどけき 春の日に』をインデックスしようとすると、『ひさかたの』『光のどけき』『春の日に』の 3 単語に分かち書きが行われるはずです。

こちらが実行結果です。
{
  "tokens": [
    {
      "token": "ひさかたの",
      "start_offset": 0,
      "end_offset": 5,
      "type": "word",
      "position": 0
    },
    {
      "token": "光のどけき",
      "start_offset": 6,
      "end_offset": 11,
      "type": "word",
      "position": 1
    },
    {
      "token": "春の日に",
      "start_offset": 12,
      "end_offset": 16,
      "type": "word",
      "position": 2
    }
  ]
}
Whitespace Tokenizer によって分かち書きされた結果が返ってきました。

全文検索時の analyzer の単語分割結果を確認する

全文検索クエリで渡される検索キーワードに適用される analyzer が、実際にどのように分かち書きを行うか確認してみます。

全文検索時の analyzer の動作は、Validate API から確認できます。Validate API 自体は全文検索クエリに間違いがないかを確認するためのものですが、レスポンスには analyzer による単語分割の結果も含まれています。

利用する Validate API のエンドポイントは /{インデックス名}/_validate/query?explain です。送信する JSON オブジェクトの内容は、全文検索クエリで実際に送るものと同じです。
$ curl -XGET http://localhost:9200/analyzer-sample01/_validate/query?explain -H "Content-type: application/json" -d '{
  "query": {
    "match" : { "my_field01" : "しづ心なく 花の散るらむ" }
  }
}'

my_field01 フィールドには、Whitespace Tokenizer の設定された analyzer が指定されているため、このフィールドに対して検索キーワード『しづ心なく 花の散るらむ』で全文検索しようとすると、検索キーワードは『しづ心なく』『花の散るらむ』の 2 単語に分かち書きが行われるはずです。
{
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "valid": true,
  "explanations": [
    {
      "index": "analyzer-sample01",
      "valid": true,
      "explanation": "my_field01:しづ心なく my_field01:花の散るらむ"
    }
  ]
}
explanations.explanation 値に、analyzer が検索キーワードを単語分割した結果が格納されます。スペース区切りで 2 単語返されていますね。
仮に analyzer が Keyword Tokenizer で単語分割したとすると、Keyword Tokenizer はスペースでは単語を分割しないので、my_field01:しづ心なく 花の散るらむ という結果が返ってきます。

インデックス時と全文検索時で異なる analyzer を設定する

それでは、転移インデックス作成時と全文検索クエリでの検索時とで適応される analyzer を変えてみます。

フィールドに search_analyzer を設定すると、全文検索時の検索キーワードに対してはその analyzer が使用されるようになります。search_analyzer の設定が適用されるのは全文検索時のみで、インデックス時の analyzer には影響がありません。つまり、フィールドの analyzer 値と search_analyzer 値に異なる analyzer を設定することで、ドキュメントの内容と検索キーワードに対して別々の分かち書きを行うことができます。
フィールドに search_analyzer を設定しないなら、これまで見てきたとおり、全文検索時には analyzer 値に設定したものが使用されます。

以下のインデックスでは、インデックス時には Whitespace Tokenizer が、全文検索時には Keyword Tokenizer が動作することになります。
$ curl -XPUT http://localhost:9200/analyzer-sample02 -H "Content-type: application/json" -d '{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_whitespace_analyzer": {
          "tokenizer": "whitespace"
        },
        "my_keyword_analyzer": {
          "tokenizer": "keyword"
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "my_field02": {
          "type": "text",
          "analyzer": "my_whitespace_analyzer",
          "search_analyzer": "my_keyword_analyzer"
        }
      }
    }
  }
}'

実際にインデックス時の analyzer の動きを確認してみます。確認したいフィールドを指定して Analyze API にリクエストします。
$ curl -XPOST http://localhost:9200/analyzer-sample02/_analyze -H "Content-type: application/json" -d '{
  "field": "my_field02",
  "text": "ちはやぶる 神代もきかず 竜田川 からくれなゐに 水くくるとは"
}'
インデックス時の analyzer による分かち書き結果は以下の通りです。
{
  "tokens": [
    {
      "token": "ちはやぶる",
      "start_offset": 0,
      "end_offset": 5,
      "type": "word",
      "position": 0
    },
    {
      "token": "神代もきかず",
      "start_offset": 6,
      "end_offset": 12,
      "type": "word",
      "position": 1
    },
    {
      "token": "竜田川",
      "start_offset": 13,
      "end_offset": 16,
      "type": "word",
      "position": 2
    },
    {
      "token": "からくれなゐに",
      "start_offset": 17,
      "end_offset": 24,
      "type": "word",
      "position": 3
    },
    {
      "token": "水くくるとは",
      "start_offset": 25,
      "end_offset": 31,
      "type": "word",
      "position": 4
    }
  ]
}
Whitespace Tokenizer によってスペース毎に分かち書きされました。

続いて全文検索時の analyzer の動きを確認してみます。確認したい全文検索クエリを指定して、Validate API にリクエストします。
$ curl -XGET http://localhost:9200/analyzer-sample02/_validate/query?explain -H "Content-type: application/json" -d '{
  "query": {
    "match" : { "my_field02" : "ちはやぶる 神代もきかず 竜田川 からくれなゐに 水くくるとは" }
  }
}'
全文検索時の analyzer による分かち書き結果は以下の通りです。
{
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "valid": true,
  "explanations": [
    {
      "index": "analyzer-sample02",
      "valid": true,
      "explanation": "my_field02:ちはやぶる 神代もきかず 竜田川 からくれなゐに 水くくるとは"
    }
  ]
}
Keyword Tokenizer が動作しているため、検索キーワードがそのまま 1 単語として分かち書きされました。

ここまでで、フィールドの analyzer 値と search_analyzer 値を変えることで、インデックス時と全文検索時で異なる analyzer を指定できることを見てきました。
ElasticSearch の古いバージョンでは、フィールドに対して index_analyzer 値を指定することで、明示的にインデックス時のみ適用される analyzer を設定できていましたが、最近のバージョンでは index_analyzer は廃止された そうです。基本的には analyzer 値を指定し、全文検索時の分かち書き方法を変更したい場合にのみ search_analyzer を指定する、というほうが仕様としてはシンプルでいいと思います。

インデックス時と全文検索時で異なるデフォルト analyzer を設定する

さきほどの例ではフィールドに対して個別にインデックス用 analyzer と全文検索用 analyzer を指定しましたが、ドキュメントタイプに対するデフォルトの analyzer についてもインデックス時と全文検索時とで異なる analyzer を設定することができます。

マッピングの settings 配下の default 値には、このマッピング内の全フィールドに適用する analyzer を指定することができます。この default 値は、これ単体だと、インデックス時と全文検索時の両方で利用され、また個別に analyzer が指定されていないすべてのフィールドに適用されます。

マッピングの settings 配下には加えて default_search 値を設定することができます。これはこのマッピング内の全フィールドに適用される search_analyzer に相当し、個別に search_analyzer が指定されていないすべてのフィールドに適用されます。

つまり、この default 値と default_search 値に異なる analyzer を設定することでも、ドキュメントの内容と検索キーワードに対して別々の分かち書きを行うことができます。

以下のインデックスでは、フィールドに対しては analyzer の指定をしていませんが、フィールドのデフォルト設定として、インデックス時には Keyword Tokenizer が、全文検索時には Whitespace Tokenizer が動作するように指定しています。
$ curl -XPUT http://localhost:9200/analyzer-sample03 -H "Content-type: application/json" -d '{
  "settings": {
    "analysis": {
      "analyzer": {
        "default": {
          "tokenizer": "keyword"
        },
        "default_search": {
          "tokenizer": "whitespace"
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "my_field03": {
          "type": "text"
        }
      }
    }
  }
}'

実際にインデックス時の analyzer の動きを確認してみます。確認したいフィールドを指定して Analyze API にリクエストします。
$ curl -XPOST http://localhost:9200/analyzer-sample03/_analyze -H "Content-type: application/json" -d '{
  "field": "my_field03",
  "text": "月見れば ちぢにものこそ 悲しけれ わが身一つの 秋にはあらねど"
}'
インデックス時の analyzer による分かち書き結果は以下の通りです。
{
  "tokens": [
    {
      "token": "月見れば ちぢにものこそ 悲しけれ わが身一つの 秋にはあらねど",
      "start_offset": 0,
      "end_offset": 32,
      "type": "word",
      "position": 0
    }
  ]
}
デフォルトのインデックス用 analyzer として Keyword Tokenizer が動作し、そのまま 1 単語として分かち書きされました。

続いて全文検索時の analyzer の動きを確認してみます。確認したい全文検索クエリを指定して、Validate API にリクエストします。
$ curl -XGET http://localhost:9200/analyzer-sample03/_validate/query?explain -H "Content-type: application/json" -d '{
  "query": {
    "match" : { "my_field03" : "月見れば ちぢにものこそ 悲しけれ わが身一つの 秋にはあらねど" }
  }
}'
全文検索時の analyzer による分かち書き結果は以下の通りです。
{
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "valid": true,
  "explanations": [
    {
      "index": "analyzer-sample03",
      "valid": true,
      "explanation": "my_field03:月見れば my_field03:ちぢにものこそ my_field03:悲しけれ my_field03:わが身一つの my_field03:秋にはあらねど"
    }
  ]
}
デフォルトの検索用 analyzer として Whitespace Tokenizer が動作しているため、検索キーワードが 5 つの単語に分かち書きされました。

古いバージョンの ElasticSearch では、マッピング内に default_index 値を指定することで、すべてのフィールドに対してデフォルトのインデックス用 analyzer を指定できました。しかし、これもフィールドに設定する index_analyzer 値と同じように、現在では廃止されています。
  ElasticSearch  コメント (1) 2018/10/12 17:43:15


公開範囲:
2020/09/11 19:15:26   ゲストさん 公開範囲: すべて, 承認済み
最高に丁寧で分かりやすいです。ものすごく助かってます
プロフィール HN: ももかん
ゲーム作ったり雑談書いたり・・・していた時期が私にもありました。
カレンダー
<<2024, 12>>
1234567
891011121314
15161718192021
22232425262728
2930311234