elasticsearch アナライザー,マッピング編


分析(アナライザー)とは

ドキュメントのテキストにインデックスが付けられるとアナライザーを使用してあとで検索しやすいようにテキストが処理、分析される。
アナライザーは文字フィルター、トークナイザー、トークンフィルターの3つの機能で構成されている。
処理の順は文字フィルター ⏩ トークナイザー ⏩ トークンフィルター である。
テキストの分析結果は検索可能なデータ構造に格納される。

機能①文字フィルター

テキストを受け取り、追加、変更、削除を行う。
例えばhtmlを受け取り、html要素を削除し、htmlエンティティを変換することができる。

※htmlエンティティとは
HTML エンティティとは、アンパサンド ( & ) で始まりセミコロン ( ; )で終わるテキスト (文字列) のひと固まりです。 エンティティは(通常は HTML コードとして解釈される)予約済み文字や、(ノーブレークスペースのように) 見えない文字を表示するためによく使用されます。(参照)

機能②トークナイザー

テキストをトークン化する。
トークン化とはテキストをトークンに分割するプロセスを指す。
例えば下記のように分割する。この際に?や:,.などは削除される。
input: " I Playing Tennis"
output: ["I" , "Playing" , "Tennis"]

機能③トークンフィルター

トークナイザが生成したトークンを受け取り、追加、削除、変更を行う。アナライザーには0個以上のトークンフィルターが含まれている可能性がある。
例えば下記のように小文字に変換する
input: ["I" , "Playing" , "Tennis"]
output: ["i" , "playing" , "tennis"]

標準アナライザーを使ってみる

POST /_analyze
{
  "text": "i play tennis 2"
  , "analyzer": "standard"
}

レスポンス
{
  "tokens" : [
    {
      "token" : "i",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "play",
      "start_offset" : 2,
      "end_offset" : 6,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "tennis",
      "start_offset" : 7,
      "end_offset" : 13,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "2",
      "start_offset" : 14,
      "end_offset" : 15,
      "type" : "<NUM>",
      "position" : 3
    }
  ]
}

アナライザーに処理させたあとはトークンごとに分割されそれぞれのトークンにメタデータが付与されている。typeの欄にはALPHANUMやNUMなどが各トークンによって区別されている。

アナライザーの各機能を指定して使用する

POST /_analyze
{
  "text": "i play tennis 2",
  "char_filter": [],
  "tokenizer": "standard",
  "filter": ["lowercase"]
}

実行結果は標準アナライザーの結果と同じである。
したがって今回実行した文字フィルター、トークナイザー、トークンフィルターが標準アナライザーとなる。

逆インデックス(転置インデックス)とは

用語とそれらを含むドキュメント間のマッピング。
用語とはアナライザーによって発行されたトークンを意味する。
トークンごとに保存されているドキュメントがわかっている状態となる。
通常の検索はdocument → 用語となるが
転地インデックスを利用してトークンを検索し保存されているドキュメントを探していくことで高速化を図っている。

テキストフィールドごとに1つの逆インデックスがある。
例えば二つのドキュメントに同じテキストフィールド(name field)があった場合、name fieldという転地インデックスが作成される。
その転地インデックスには二つのドキュメントのname fieldに格納されている全てのトークンと格納されているドキュメントが結びつけられている(マッピング)。

テキストフィールドが分析されると結果の用語は逆インデックスに格納される。逆インデックスはElasticsearchが構築するApache Luceneによって作成および保守される。

マッピングとは

ドキュメントの構造とドキュメントのインデックス作成と保存の方法を定義します。
マッピングには基本的に2つのアプローチがある。
明示的なマッピング、動的なマッピングである。

データ型の種類

オブジェクト型、ネスト型、キーワード型、テキスト型がある。

テキスト型

テキストをトークンに分けたりすることで部分検索ができる。

オブジェクト型

オブジェクト型とはjsonそのもので、valueの値がjson形式になっているものである。

キーワード型

POST /_analyze
{
  "text": "i play tennis 2",
  "analyzer": "keyword"
}

レスポンス 
{
  "tokens" : [
    {
      "token" : "i play tennis 2",
      "start_offset" : 0,
      "end_offset" : 15,
      "type" : "word",
      "position" : 0
    }
  ]
}

上記がキーワードアナライザーを使用した結果である。
キーワード型はソート、フィルタリング、集計を行うデータ型である。
キーワードデータタイプを使用するフィールドはキーワードアナライザーを使用する。このアナライザーは全ての入力文字列を単一のトークンとして出力する。単一のトークンは転置インデックスに配置される。そのため、キーワードデータタイプは完全一致や並べ替えなどに使用される。完全一致などはテキストフィールドのような文字列をトークンごとに分けてしまっては検索できない。

配列型

ドキュメント追加時に配列に文字を格納して登録するだけである。
配列はテキスト型として認識される。
配列はテキスト型として認識されるためアナライズされた結果はトークンに分割される。ちなみにオフセットは配列の最初から数えられている。
ただし、登録する際にマッピングを指定していない場合は、配列の中は全て同じデータ型でなければいけない。マッピングを指定している場合は同じデータ型でなくて良い。

データ型を強制する


登録する際にマッピングをfloatとして登録していたとする。
そこに"9.1"というテキストを登録しようとした場合、変換できそうであればfloatに変換され登録されるというものである。
通常にmappingでフィールドの型を指定せずに登録した場合は
浮動小数点または文字列のいずれかになる場合がある。
重要なのは常にデータタイプを指定すること。想定されているデータ型でなければエラーが起こる設定にすることもできる。

明示的なマッピングの追加

PUT /new_inde (方法1)
{
  "mappings": {
    "properties": {
      "price": { "type": "float"},
      "meta":{
        "properties": {
          "name": { "type": "text"}
        }
      }
    }
  }
}
PUT /new_inde (方法2)
{
  "mappings": {
    "properties": {
      "price": { "type": "float"},
      "meta.name": { "type": "test"}
    }
  }
}

レスポンス
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "new_inde"
}

マッピングはpropertiesフィールドの中に記述していく。
metaフィールドはネストしているがこの場合は再度propertiesフィールドを記述する。

マッピングを取得

GET /new_inde/_mapping  (フィールド指定なし)

レスポンス
{
  "new_inde" : {
    "mappings" : {
      "properties" : {
        "meta" : {
          "properties" : {
            "name" : {
              "type" : "text"
            }
          }
        },
        "price" : {
          "type" : "float"
        }
      }
    }
  }
}

GET /new_inde/_mapping/field/price (フィールド指定)
GET /new_inde/_mapping/field/meta.name (フィールド指定)

すでにあるインデックスにマッピングの追加

PUT /new_inde/_mapping
{
  "properties": {
    "new_field": {"type": "text"}
  }
}

マッピングのフォーマットパラメーター

PUT /new_inde/_mapping
{
  "properties": {
    "created_at": {
      "type": "date",
      "format": "dd/MM/yyyy"
    }
  }
}

例えば日付などで自分でフォーマットを指定することができる。

マッピングのプロパティパラメーター

プロパティパラメーターは今までも使用しており、マッピング指定の一番上の階層で使用したり、ネストされたオブジェクトがある際に使用する。

マッピングのcoerceパラメーター

PUT /new_inde/_mapping
{
  "properties": {
    "new_field": {
      "type": "text",
      "coerce": false
    }
  }
}

このパラメーターは型強制を有効、無効の設定をできる。

マッピングの更新

PUT /to_index/_mapping (新規のインデックスを作成、マッピング)
{
  "properties": {
    "new_field": {
      "type": "keyword",
      "ignore_above": 256
    }
  }
}

POST /_reindex
{
  "source": {
    "index": "from_index"
  },
  "dest": {
    "index": "to_index"
  }
}

例えばテキスト型で登録されているのをキーワード型にする場合はデータ構造の変換、つまりインデックスの新規のインデックスを作成し、既存のドキュメントの追加を行う必要があります。
そういう時に使用するのがReindexというAPIです。このAPIはドキュメントをインデックスから別のインデックスに移すAPIです。
Reindexはクエリなどを指定すれば指定したクエリでヒットしたのみのインデックスを作成することができます。
"ignore_above": 256は256文字以上超えたら逆インデックスに登録しないことを指しています。

フィールドの名前変更

PUT /new_inde/_mapping
{
  "properties": {
    "commat": {
      "type": "alias",
      "path": "content"
    }
  }
}

commatという名前のフィールドをcontentというフィールドに変更する

マルチフィールドとは

PUT /multi_field
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

マルチフィールドの設定を行えばテキストタイプとキーワードタイプをマッピングすることができる。これにより全文検索および集計、ソートなどもできるようになり
クエリの幅も広がる。

インデックスパターンとは

PUT /_template/logs
{
  "index_patterns": ["logs*"],
  "settings":{
    "number_of_shards":1
  },
  "mappings": {
    "properties": {
      "name":{
        "type": "text"
      }
    }
  }
}

新規にインデックスを作成する前にインデックスのシャードの初期設定やマッピングなどを指定したテンプレートを作成しておく。
"index_patterns"を登録することにより新規に作成するインデックス名がパターンに合致していたらテンプレートを採用する。インデックステンプレートはGETコマンドで取得することができる。

ECSとは

Elasticsearchには色々なアプリケーションからデータを保存するためのリクエストが送られてくるが異なるデータの場合でも柔軟なデータ構造を提供するもの

ダイナミックマッピングとは

明示的にマッピングを指定していせずにドキュメントを追加した時に自動でフィールドのタイプが生成される。テキストの場合はテキスト型とキーワード型で設定される。

ダイナミックマッピングの無効

PUT /people
{
  "mappings": {
    "dynamic": false,
    "properties": {
      "name": {
        "type": "text"
      }
    }
  }
}

elasticsearchはdynamicの設定をfalseにした場合、新規のフィールドを無視します。