1. 前述
嵌套类型是对象数据类型的一个特殊化版本,它允许对象数组以一种可以相互独立查询(内嵌关系,对象内关联不丢失)的方式进行索引。
2. 内部对象数组是如何进行展平的(未使用nested字段)
内部对象数组字段的工作方式可能与你预期的不同。Lucene是没有内部对象的概念的。Elasticsearch将对象层次结构展平为一个简单的字段名和值列表。例如,以下文档:
2.1 定义索引类型(内部对象)
PUT inner_object_index/_doc/1{"group" : "stars","user" : [{"first" : "张","last" : "学友"},{"first" : "刘","last" : "德华"},{"first" : "郭","last" : "富城"},{"first" : "黎","last" : "明"}]}
在ES内部,将上述文档转换为更扁平化的文档,如下
{"group" : "fans","user.first" : [ "张", "刘", "郭", "黎" ],"user.last" : [ "学友", "德华", "富城", "明" ]}
user.first和user.last字段被展平为多值字段,以“刘德华”为例,姓氏“刘”和名字“德华”之间的关联将丢失,下面实例查询姓氏为“刘”名字为“富城”(文档中并没有“刘富城”这个人)的文档:
2.2 查询文档
GET inner_object_index/_search{"query": {"bool": {"must": [{ "match": { "user.first": "刘" }},{ "match": { "user.last": "德华" }}]}}}
2.3 查询结果
{"took" : 0,"timed_out" : false,"_shards" : {"total" : 5,"successful" : 5,"skipped" : 0,"failed" : 0},"hits" : {"total" : 1,"max_score" : 0.8630463,"hits" : [{"_index" : "nested_object_index","_type" : "_doc","_id" : "1","_score" : 0.8630463,"_source" : {"group" : "stars","user" : [{"first" : "张","last" : "学友"},{"first" : "刘","last" : "德华"},{"first" : "郭","last" : "富城"},{"first" : "黎","last" : "明"}]}}]}}
3. 对象数组使用嵌套字段定义(使用nested字段)
如果需要索引对象数组并保持数组中每个对象的独立性,应该使用嵌套数据类型(nested datatype)而不是对象数据类型(object datatype)。在内部,嵌套对象将数组中的每个对象索引为单独的隐藏文档,这意味着可以使用嵌套查询,独立于其他对象查询每个嵌套对象:
3.1 定义索引类型
PUT nested_objects_index{"mappings": {"_doc": {"properties": {"group":{"type":"keyword"},"user": {"type": "nested"}}}}}
3.2 插入测试数据
PUT nested_objects_index/_doc/1{"group" : "stars","user" : [{"first" : "张","last" : "学友"},{"first" : "刘","last" : "德华"},{"first" : "郭","last" : "富城"},{"first" : "黎","last" : "明"}]}
3.3 查询示例1及结果1(嵌套查询)
查询姓氏为“刘”名字为“富城”(“刘”“德华”不是同一个内嵌对象)的文档:
GET nested_objects_index/_search{"query":{"nested":{"path":"user","query":{"bool":{"must":[{"match":{"user.first":"刘"}},{"match":{"user.last":"富城"}}]}}}}}
{"took" : 0,"timed_out" : false,"_shards" : {"total" : 5,"successful" : 5,"skipped" : 0,"failed" : 0},"hits" : {"total" : 0,"max_score" : null,"hits" : [ ]}}
由于文档中并没有“刘富城”这个人,查询结果为空,显然,使用nested定义的字段,在ES内部,将对象数组扁平化的过程中,对象属性之间的关联关系不会发生改变
3.4 查询示例2及结果2(嵌套查询,突出显示)
查询姓氏为“刘”名字为“德华”(“刘”“德华”为同一个内嵌对象)的文档:
GET nested_objects_index/_search{"query": {"nested": {"path": "user","query": {"bool": {"must": [{ "match": { "user.first": "刘" }},{ "match": { "user.last": "德华" }}]}},"inner_hits": {"highlight": {"fields": {"user.first": {},"user.last": {}}}}}}}
查询结果如下:
{"took" : 2,"timed_out" : false,"_shards" : {"total" : 5,"successful" : 5,"skipped" : 0,"failed" : 0},"hits" : {"total" : 1,"max_score" : 3.4789643,"hits" : [{"_index" : "nested_objects_index","_type" : "_doc","_id" : "1","_score" : 3.4789643,"_source" : {"group" : "stars","user" : [{"first" : "张","last" : "学友"},{"first" : "刘","last" : "德华"},{"first" : "郭","last" : "富城"},{"first" : "黎","last" : "明"}]},"inner_hits" : {"user" : {"hits" : {"total" : 1,"max_score" : 3.4789643,"hits" : [{"_index" : "nested_objects_index","_type" : "_doc","_id" : "1","_nested" : {"field" : "user","offset" : 1},"_score" : 3.4789643,"_source" : {"first" : "刘","last" : "德华"},"highlight" : {"user.first" : ["<em>刘</em>"],"user.last" : ["<em>德</em><em>华</em>"]}}]}}}}]}}
4. 嵌套文档使用
- 使用嵌套查询(nested query)查询
- 使用嵌套(
nested)和反向嵌套(reverse_nested)聚合进行分析。 - 使用嵌套排序( nested sorting)进行排序
- 检索并突出显示嵌套命中对象( nested inner hits)。
5. 嵌套定义参数分析
| 参数名称 | 参数说明 | |
|---|---|---|
dynamic |
是否支持将新属性动态添加到现有嵌套对象。接收true(默认)、false和strict。 | |
properties |
嵌套对象中的字段,可以是任何数据类型,包括嵌套类型(即嵌套对象里面定义嵌套字段)。可以将新properties添加到现有嵌套对象中。 |
提示:由于嵌套文档作为单独的文档编制索引,因此只能在嵌套查询(nested query)、嵌套/反向嵌套聚合(nested/reverse_nested aggregations)或嵌套内部命中(nested inner hits)的范围内访问它们。
6. 嵌套字段数量限制
为包含100个嵌套字段的文档编制索引,实际上是为101个文档编制索引,因为每个嵌套文档都作为单独的文档编制索引。为了防止错误定义的映射(mapping),每个索引可以定义的嵌套字段的数量限制为50个。
7. 防止映射爆炸(mapping explosion)的设置
在索引中定义太多字段可能导致映射爆炸,这种爆炸可能导致内存不足错误和数据难以恢复。这个问题可能比预期的更常见。例如,考虑这样一种情况:插入的每个新文档都会引入新字段。这在动态映射(dynamic mappings)中很常见。每次文档包含新字段时,这些字段都会出现在索引的映射中。对于量数据少这并不担心,但随着映射的增长,这可能会成为一个问题。以下设置允许您限制手动或动态创建的字段映射的数量,以防止错误文档导致映射爆炸:
| 配置项 | 配置项说明 | |
|---|---|---|
| index.mapping.total_fields.limit | 索引中字段的最大数目。字段和对象映射(object mappings)以及字段别名(field aliases)都将计入此限制。默认值为1000。 | |
| index.mapping.depth.limit | 字段的最大深度。以inner objects的数量来衡量。例如,如果所有字段都在根对象级别定义,则深度为1。如果有一个对象映射,则深度为2,以此类推。默认值为20。 | |
| index.mapping.nested_fields.limit | 索引中嵌套字段的最大数目,默认为50。为一个包含100个嵌套字段的文档编制索引实际上是为101个文档编制索引,因为每个嵌套文档都作为单独的隐藏文档编制索引 |
