嵌套字段类型
编辑嵌套字段类型编辑
nested
类型是 object
数据类型的特殊版本,它允许以可以彼此独立查询的方式对对象数组进行索引。
当使用大量任意键集合提取键值对时,您可能会考虑将每个键值对建模为其自己的嵌套文档,并使用 key
和 value
字段。 您可以考虑使用 扁平化 数据类型,它将整个对象映射为单个字段,并允许对其内容进行简单搜索。 嵌套文档和查询通常开销很大,因此对于此用例,使用 flattened
数据类型是更好的选择。
嵌套字段在 Kibana 中的支持不完整。 虽然它们在 Discover 中可见且可搜索,但不能用于在 Lens 中构建可视化。
对象数组如何扁平化编辑
Elasticsearch 没有内部对象的 própriété。 因此,它将对象层次结构扁平化为字段名称和值的简单列表。 例如,请考虑以下文档
resp = client.index( index="my-index-000001", id="1", body={ "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)
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", body={ "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)
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", body={"mappings": {"properties": {"user": {"type": "nested"}}}}, ) print(resp) resp = client.index( index="my-index-000001", id="1", body={ "group": "fans", "user": [ {"first": "John", "last": "Smith"}, {"first": "Alice", "last": "White"}, ], }, ) print(resp) resp = client.search( index="my-index-000001", body={ "query": { "nested": { "path": "user", "query": { "bool": { "must": [ {"match": {"user.first": "Alice"}}, {"match": {"user.last": "Smith"}}, ] } }, } } }, ) print(resp) resp = 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": {}}} }, } } }, ) print(resp)
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) }
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
以允许在突出显示期间使用 postings,则这些偏移量在主要突出显示阶段将不可用。 相反,需要通过 嵌套内部匹配 执行突出显示。 当在搜索期间通过 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
对象的总数必须低于限制。
有关防止映射爆炸的其他设置,请参阅 防止映射爆炸的设置。