ドキュメントの保存と検索はざっくりとわかったので、マッピングについて調べました。
マッピングとは
ドキュメントがどのように保存され、どのようにインデックスが作成されるかを定義するのがマッピングらしいです。RDBで言うスキーマかなと最初思ったのですが、だいぶ違うものみたいなので同じようなものとは考えないほうがいいと思います。
公式サイトのドキュメントでは以下のように説明がありました。
Mapping is the process of defining how a document, and the fields it contains, are stored and indexed.
マッピングは以下の2つの定義を持っています。
- Meta-fields - ドキュメントに関連付けられたメタデータをどのように扱うかを設定するために使用
- Fields or properties - ドキュメントに関連するフィールドか、プロパティのリスト
定義だけだとわかりにくいので、実際に触って見る必要がありそうです。
マッピングを静的に設定
マッピングは動的または静的に設定できます。ここでは静的に設定してみます。
マッピングの設定は REST API で行うことができるようです。インデックスを作成すると同時にマッピングを指定します。
PUT /employee { "mappings": { "properties": { "name": { "type": "text" }, "age": { "type": "integer" }, "belog": { "type": "keyword" } } } }
プロパティとしてドキュメントが持つフィールドとその格納方法を指定しました。
どのようなマッピングになっているかは以下のリクエストで確認できます。
GET /employee/_mapping
{ "employee" : { "mappings" : { "properties" : { "age" : { "type" : "integer" }, "belog" : { "type" : "keyword" }, "name" : { "type" : "text" } } } } }
マッピングが正しく設定されているかを検証してみました。 text
と keyword
フィールドはそれぞれデータの持ち方が異なります。 text
は全文検索のために Elasticsearch が文字列を解析しますが、 keyword
の場合は文字列全体を一つの値として扱うみたいです。ざっくりと説明すると、 text
の場合は文字列が要素(動詞とか、形容詞とか)に分割されて、keyword
はされないといった感じでしょうか。
text
フィールドに対して全文検索すると、文字の一部分が一致するだけでヒットするようです(一部分という表現が曖昧なのはよくわかっていないからです。このあたりは後で調査)。全文検索には match
句を使います。
POST /employee/_doc { "name": "BOOK STORE", "age": 27, "belong": "normal deveoper" }
GET /employee/_search { "query": { "match": { "name": "BOOK" } } }
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.2876821, "hits" : [ { "_index" : "employee", "_type" : "_doc", "_id" : "_tZGCnMBWU8rPLHxmgBS", "_score" : 0.2876821, "_source" : { "name" : "BOOK STORE", "age" : 27, "belong" : "normal developper" } } ] } }
keyword
フィールドは文字列が全部一致しないと検索結果に表示されません。 keyword
フィールドに対する文字列の検索では term
句を使用します。検索結果は長いので省略します。
GET /employee/_search { "query": { "term": { "belong": { "value": "normal" } } } }
文字列全部が一致していれば、ヒットします。
GET /employee/_search { "query": { "term": { "belong": { "value": "normal developper" } } } }
ちなみに、フィールドのデータ・タイプは以下に網羅されています。詳細な定義や説明も載ってます。
複数フィールドの設定
Elasticsearch では一つのフィールドに対して、データ・タイプを複数設定することが可能です。例えば text
と keyword
を同時に定義し、検索するときにどちらのデータ・タイプとして使用するか、指定します。複数のデータ・タイプを持てることによって、検索がしやすくできるということでしょうね。
というわけで、上記でやってみたマッピング定義を再び使い、複数フィールドの設定をしてみます。
一度インデックスを削除。
DELETE /employee
再び、インデックスを作成すると同時にマッピングを定義します。
PUT /employee { "mappings": { "properties": { "name": { "type": "text" }, "age": { "type": "integer" }, "belong": { "type": "text", "fields": { "raw": { "type": "keyword", "ignore_above": 256 } } } } } }
上記では belong
フィールドに複数のデータ・タイプを定義しました。 fields
という項目がありますが、この項目を指定することで複数のデータ・タイプを定義できます。 fields
には2つ目のデータ・タイプと、2つ目の名前を定義します。上記では raw
という名前で keyword
データ・タイプを定義しました。 ignore_above
という項目は keyword
データ・タイプを使用するときに追加で指定できる項目で、文字列の長さが指定数以上のときにインデックスを作らない設定ができます(おそらく負荷の関係?)。
検索はこうなります。
belong
を text
として使う場合。
GET /employee/_search { "query": { "match": { "belong": "normal" } } }
belong
を keyword
として使う場合。
GET /employee/_search { "query": { "term": { "belong.raw": { "value": "normal developper" } } } }
メタフィールド
マッピングではメタフィールドの振る舞いを設定できるみたいです。メタフィールドは全て _
で始まる規則があるようで、検索結果の値に含まれています。また、検索にも使用することができます。
メタフィールドは以下のページに一覧と詳しい解説があります。
検索結果に含まれているメタフィールドは例えばこんな感じかなと。
{ (省略) "hits" : [ { "_index" : "employee", "_type" : "_doc", "_id" : "ifZgDXMBoJXfK5lP7faD", "_score" : 1.0, "_source" : { "name" : "BOOK STORE", "age" : 27, "belong" : "normal developper" } } ] } }
ドキュメントの一意のIDを表す _id
メタフィールドの値を使用した検索はこうなります。IDの指定が配列になっているのは複数のIDを検索できるからですね。
GET employee/_search { "query": { "terms": { "_id": ["ifZgDXMBoJXfK5lP7faD"] } } }
_id
メタフィールドのID値は Get API でも使用されます。
GET employee/_doc/ifZgDXMBoJXfK5lP7faD
_source
メタフィールドはドキュメント本体を示すメタフィールドになります。マッピングの定義で _source
に含める、含めないフィールドを設定することができるようです。あくまでも _source
に含まれるかを設定できるようなので、含まれていない値を使った検索もできます。
// age フィールドを除外 PUT employee { "mappings": { "_source": { "excludes": [ "age" ] } } } // ドキュメントを追加。除外されるフィールドがあってもいい。 POST /employee/_doc { "name": "BOOK STORE", "age": 27, "belong": "normal developper" } // 除外されているフィールドを使って検索 GET employee/_search { "query": { "term": { "age": "27" } } } // 検索結果に age は含まれない { "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "employee", "_type" : "_doc", "_id" : "1BnyDXMBkS7Mh8swfSKP", "_score" : 1.0, "_source" : { "belong" : "normal developper", "name" : "BOOK STORE" } } ] } }