嵌套字段类型
编辑嵌套字段类型
编辑nested
类型是 object
数据类型的专门版本,它允许以可以彼此独立查询的方式索引对象数组。
当摄取具有大量任意键的键值对时,您可以考虑将每个键值对建模为具有 key
和 value
字段的嵌套文档。相反,请考虑使用 flattened 数据类型,该类型将整个对象映射为单个字段,并允许对其内容进行简单搜索。嵌套文档和查询通常开销很大,因此对于这种情况,使用 flattened
数据类型是更好的选择。
嵌套字段在 Kibana 中的支持不完整。虽然它们在 Discover 中可见且可搜索,但不能用于在 Lens 中构建可视化。
对象数组如何被扁平化
编辑Elasticsearch 没有内部对象的概念。因此,它将对象层次结构展平为简单的字段名称和值列表。例如,考虑以下文档
resp = client.index( index="my-index-000001", id="1", document={ "group": "fans", "user": [ { "first": "John", "last": "Smith" }, { "first": "Alice", "last": "White" } ] }, ) print(resp)
response = client.index( index: 'my-index-000001', id: 1, body: { group: 'fans', user: [ { first: 'John', last: 'Smith' }, { first: 'Alice', last: 'White' } ] } ) puts response
res, err := es.Index( "my-index-000001", strings.NewReader(`{ "group": "fans", "user": [ { "first": "John", "last": "Smith" }, { "first": "Alice", "last": "White" } ] }`), es.Index.WithDocumentID("1"), es.Index.WithPretty(), ) fmt.Println(res, err)
const response = await client.index({ index: "my-index-000001", id: 1, document: { group: "fans", user: [ { first: "John", last: "Smith", }, { first: "Alice", last: "White", }, ], }, }); console.log(response);
PUT my-index-000001/_doc/1 { "group" : "fans", "user" : [ { "first" : "John", "last" : "Smith" }, { "first" : "Alice", "last" : "White" } ] }
先前的文档将在内部转换为更像这样的文档
{ "group" : "fans", "user.first" : [ "alice", "john" ], "user.last" : [ "smith", "white" ] }
user.first
和 user.last
字段被展平为多值字段,并且 alice
和 white
之间的关联丢失。此文档将错误地匹配 alice AND smith
的查询
resp = client.search( index="my-index-000001", query={ "bool": { "must": [ { "match": { "user.first": "Alice" } }, { "match": { "user.last": "Smith" } } ] } }, ) print(resp)
response = client.search( index: 'my-index-000001', body: { query: { bool: { must: [ { match: { 'user.first' => 'Alice' } }, { match: { 'user.last' => 'Smith' } } ] } } } ) puts response
res, err := es.Search( es.Search.WithIndex("my-index-000001"), es.Search.WithBody(strings.NewReader(`{ "query": { "bool": { "must": [ { "match": { "user.first": "Alice" } }, { "match": { "user.last": "Smith" } } ] } } }`)), es.Search.WithPretty(), ) fmt.Println(res, err)
const response = await client.search({ index: "my-index-000001", query: { bool: { must: [ { match: { "user.first": "Alice", }, }, { match: { "user.last": "Smith", }, }, ], }, }, }); console.log(response);
GET my-index-000001/_search { "query": { "bool": { "must": [ { "match": { "user.first": "Alice" }}, { "match": { "user.last": "Smith" }} ] } } }
对对象数组使用 nested
字段
编辑如果您需要索引对象数组并保持数组中每个对象的独立性,请使用 nested
数据类型而不是 object
数据类型。
在内部,嵌套对象将数组中的每个对象索引为单独的隐藏文档,这意味着可以使用 nested
查询独立查询每个嵌套对象
resp = client.indices.create( index="my-index-000001", mappings={ "properties": { "user": { "type": "nested" } } }, ) print(resp) resp1 = client.index( index="my-index-000001", id="1", document={ "group": "fans", "user": [ { "first": "John", "last": "Smith" }, { "first": "Alice", "last": "White" } ] }, ) print(resp1) resp2 = client.search( index="my-index-000001", query={ "nested": { "path": "user", "query": { "bool": { "must": [ { "match": { "user.first": "Alice" } }, { "match": { "user.last": "Smith" } } ] } } } }, ) print(resp2) resp3 = client.search( index="my-index-000001", query={ "nested": { "path": "user", "query": { "bool": { "must": [ { "match": { "user.first": "Alice" } }, { "match": { "user.last": "White" } } ] } }, "inner_hits": { "highlight": { "fields": { "user.first": {} } } } } }, ) print(resp3)
response = client.indices.create( index: 'my-index-000001', body: { mappings: { properties: { user: { type: 'nested' } } } } ) puts response response = client.index( index: 'my-index-000001', id: 1, body: { group: 'fans', user: [ { first: 'John', last: 'Smith' }, { first: 'Alice', last: 'White' } ] } ) puts response response = client.search( index: 'my-index-000001', body: { query: { nested: { path: 'user', query: { bool: { must: [ { match: { 'user.first' => 'Alice' } }, { match: { 'user.last' => 'Smith' } } ] } } } } } ) puts response response = client.search( index: 'my-index-000001', body: { query: { nested: { path: 'user', query: { bool: { must: [ { match: { 'user.first' => 'Alice' } }, { match: { 'user.last' => 'White' } } ] } }, inner_hits: { highlight: { fields: { 'user.first' => {} } } } } } } ) puts response
{ res, err := es.Indices.Create( "my-index-000001", es.Indices.Create.WithBody(strings.NewReader(`{ "mappings": { "properties": { "user": { "type": "nested" } } } }`)), ) fmt.Println(res, err) } { res, err := es.Index( "my-index-000001", strings.NewReader(`{ "group": "fans", "user": [ { "first": "John", "last": "Smith" }, { "first": "Alice", "last": "White" } ] }`), es.Index.WithDocumentID("1"), es.Index.WithPretty(), ) fmt.Println(res, err) } { res, err := es.Search( es.Search.WithIndex("my-index-000001"), es.Search.WithBody(strings.NewReader(`{ "query": { "nested": { "path": "user", "query": { "bool": { "must": [ { "match": { "user.first": "Alice" } }, { "match": { "user.last": "Smith" } } ] } } } } }`)), es.Search.WithPretty(), ) fmt.Println(res, err) } { res, err := es.Search( es.Search.WithIndex("my-index-000001"), es.Search.WithBody(strings.NewReader(`{ "query": { "nested": { "path": "user", "query": { "bool": { "must": [ { "match": { "user.first": "Alice" } }, { "match": { "user.last": "White" } } ] } }, "inner_hits": { "highlight": { "fields": { "user.first": {} } } } } } }`)), es.Search.WithPretty(), ) fmt.Println(res, err) }
const response = await client.indices.create({ index: "my-index-000001", mappings: { properties: { user: { type: "nested", }, }, }, }); console.log(response); const response1 = await client.index({ index: "my-index-000001", id: 1, document: { group: "fans", user: [ { first: "John", last: "Smith", }, { first: "Alice", last: "White", }, ], }, }); console.log(response1); const response2 = await client.search({ index: "my-index-000001", query: { nested: { path: "user", query: { bool: { must: [ { match: { "user.first": "Alice", }, }, { match: { "user.last": "Smith", }, }, ], }, }, }, }, }); console.log(response2); const response3 = await client.search({ index: "my-index-000001", query: { nested: { path: "user", query: { bool: { must: [ { match: { "user.first": "Alice", }, }, { match: { "user.last": "White", }, }, ], }, }, inner_hits: { highlight: { fields: { "user.first": {}, }, }, }, }, }, }); console.log(response3);
PUT my-index-000001 { "mappings": { "properties": { "user": { "type": "nested" } } } } PUT my-index-000001/_doc/1 { "group" : "fans", "user" : [ { "first" : "John", "last" : "Smith" }, { "first" : "Alice", "last" : "White" } ] } GET my-index-000001/_search { "query": { "nested": { "path": "user", "query": { "bool": { "must": [ { "match": { "user.first": "Alice" }}, { "match": { "user.last": "Smith" }} ] } } } } } GET my-index-000001/_search { "query": { "nested": { "path": "user", "query": { "bool": { "must": [ { "match": { "user.first": "Alice" }}, { "match": { "user.last": "White" }} ] } }, "inner_hits": { "highlight": { "fields": { "user.first": {} } } } } } }
与 nested
文档交互
编辑嵌套文档可以
- 使用
nested
查询进行查询。 - 使用
nested
和reverse_nested
聚合进行分析。 - 使用嵌套排序进行排序。
- 使用嵌套内部命中进行检索和突出显示。
由于嵌套文档被索引为单独的文档,因此它们只能在 nested
查询、nested
/reverse_nested
聚合或 嵌套内部命中的范围内访问。
例如,如果嵌套文档中的字符串字段的 index_options
设置为 offsets
以允许在突出显示期间使用帖子,则这些偏移量在主突出显示阶段将不可用。相反,突出显示需要通过 嵌套内部命中执行。当在搜索期间通过docvalue_fields
或 stored_fields
加载字段时,也适用相同的考虑因素。
nested
字段的参数
编辑nested
字段接受以下参数
-
dynamic
- (可选,字符串)是否应将新的
properties
动态添加到现有的嵌套对象。接受true
(默认)、false
和strict
。 -
properties
- (可选,对象)嵌套对象中的字段,可以是任何数据类型,包括
nested
。新属性可以添加到现有的嵌套对象中。 -
include_in_parent
- (可选,布尔值)如果为
true
,则嵌套对象中的所有字段也会作为标准(扁平)字段添加到父文档中。默认为false
。 -
include_in_root
- (可选,布尔值)如果为
true
,则嵌套对象中的所有字段也会作为标准(扁平)字段添加到根文档中。默认为false
。
nested
映射和对象的限制
编辑如前所述,每个嵌套对象都作为单独的 Lucene 文档进行索引。继续前面的示例,如果我们索引一个包含 100 个 user
对象的单个文档,则会创建 101 个 Lucene 文档:一个用于父文档,一个用于每个嵌套对象。由于 nested
映射的开销,Elasticsearch 设置了一些设置以防止出现性能问题
-
index.mapping.nested_fields.limit
- 索引中不同
nested
映射的最大数量。nested
类型应仅在特殊情况下使用,当需要彼此独立查询对象数组时。为了防止设计不佳的映射,此设置限制了每个索引的唯一nested
类型数量。默认为50
。
在前面的示例中,user
映射仅计入此限制中的 1 个。
-
index.mapping.nested_objects.limit
- 单个文档可以在所有
nested
类型中包含的最大嵌套 JSON 对象数。此限制有助于防止当文档包含太多嵌套对象时出现内存不足错误。默认为10000
。
为了说明此设置的工作原理,请考虑向先前的示例映射添加另一个名为 comments
的 nested
类型。对于每个文档,它包含的 user
和 comment
对象总数必须低于限制。
有关防止映射爆炸的其他设置,请参阅防止映射爆炸。