本記事でやること
Elasticsearchでは、mappingで定義した型とは異なる型のデータをインデキシングしてもエラーにならないことがあります。本記事では、以下の2点を軸にその理由を検証します。
-
coerceパラメータがデフォルトで
true
になっており、フィールドの型にあうように変換してくれる -
_source
フィールドはインデキシング時の「元のデータ」を保持・表示するためmappingの型と異なるように"見える"
実際にcoerce
をtrue
/false
のmappingを用いて異なる型のデータをインデキシングしてエラーの有無や検索結果の挙動を比較します。
対象読者
- Elasticsearchのmappingを設計・管理している方
- 型の厳密性やバリデーションに興味がある方
背景
mappingと異なる型のデータをインデキシングした時エラーにならなかったこと、さらに検索した時に異なる型のデータとして検索結果に表示されることがありこの挙動に疑問を感じました。
型が異なるデータをインデキシングしてもエラーにならない理由
これは、mappingのフィールドを定義する際にcoerceパラメータがデフォルトでtrue
に設定されているためです。
coerce
とは?
- 異なる型の値をフィールドの型に合うように変換してくれる機能
実験
以下のようなmappingとドキュメントを使って検証を行います。
1. coerce:true
のmapping
integer型としてフィールドを定義
PUT /test-coerce-true
{
"mappings": {
"properties": {
"price": {
"type": "integer",
"coerce": true
}
}
}
}
異なる型(文字列)の値をインサートしても成功します。
// ドキュメントの追加
POST /test-coerce-true/_doc
{
"price": "100"
}
integer型の値で検索してもドキュメントにヒットします。
GET /test-coerce-true/_search
{
"query": {
"range": {
"price": {
"lte":200
}
}
}
}
// 検索結果
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "test-coerce-true",
"_id": "SuLK9JcB2IXIFQK-oa7b",
"_score": 1,
"_source": {
"price": "100"
}
}
]
}
}
2. coerce:false
のmapping
PUT /test-coerce-false
{
"mappings": {
"properties": {
"price": {
"type": "integer",
"coerce": false
}
}
}
}
異なる型(文字列)の値をインサートするとエラーが発生
POST /test-coerce-false/_doc
{
"price": "100"
}
型が異なることでdocument_parsing_exception
が発生しています。
{
"error": {
"root_cause": [
{
"type": "document_parsing_exception",
"reason": "[2:12] failed to parse field [price] of type [integer] in document with id 'TOLQ9JcB2IXIFQK-sq4X'. Preview of field's value: '100'"
}
],
"type": "document_parsing_exception",
"reason": "[2:12] failed to parse field [price] of type [integer] in document with id 'TOLQ9JcB2IXIFQK-sq4X'. Preview of field's value: '100'",
"caused_by": {
"type": "illegal_argument_exception",
"reason": "Integer value passed as String"
}
},
"status": 400
}
型が異なるように見える理由
coerce
をtrue
に設定した時、検索結果(_source
フィールド)には文字列の値が表示されています。
GET /test-coerce-true/_search
{
"query": {
"range": {
"price": {
"lte":200
}
}
}
}
coerce
パラメータによって、フィールドの型に合わせて変換されたはずなのになぜ_source
フィールドには文字列が表示されているのでしょうか。
// 検索結果
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "test-coerce-true",
"_id": "SuLK9JcB2IXIFQK-oa7b",
"_score": 1,
"_source": {
"price": "100" // 文字列の値が表示されている
}
}
]
}
}
_source
フィールドはインデキシング時のデータを表示しているため文字列のままになっています。Elasticsearch内部でどの型で扱われているかとは無関係です。
The _source field contains the original JSON document body that was passed at index time.
まとめ
-
Elasticsearchでは、mappingの型と異なるデータを投入してもエラーにならないことがある
-
その主な理由は、デフォルトで
coerce: true
が有効になっており、自動的に型変換が行われるから -
検索時に表示される
_source
は「投入したデータそのもの」であり、mappingの型とは異なることがある -
型の整合性を厳密に保ちたい場合は、
coerce: false
を利用するのが効果的