原英文版地址: https://www.elastic.co/guide/en/elasticsearch/reference/7.7/nested.html, 原文档版权归 www.elastic.co 所有
本地英文版地址: ../en/nested.html

nested(嵌套)数据类型

nested类型是object数据类型的一个特殊版本,它允许以一种可以相互独立查询的方式对对象数组进行索引。

当摄入包含大量任意键的键值对时,可以考虑将每个键值对建模为包含keyvalue字段的嵌套文档。 相反,可以考虑使用flattened数据类型,它将整个对象映射为单个字段,并允许对其内容进行简单的搜索。 嵌套的文档及其查询的代价通常是很高的,因此对于这个用例使用flattened数据类型是一个更好的选择。

对象数组是如何扁平化的?

Elasticsearch没有内部对象的概念。 因此,它将对象层次结构简化为字段名称和值的简单列表。 例如,考虑以下文档:

PUT my_index/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

字段user是自动添加的,其字段类型为object

上面的文档将在内部转换成如下所示的文档:

{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}

user.firstuser.last字段被展平为多值字段,alicewhite之间的关联丢失。 该文档将错误地匹配查询alice AND smith

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}

nested字段用于对象数组

如果需要索引对象数组并保持数组中每个对象的独立性,请使用nested数据类型而不是object数据类型。

在内部,嵌套对象将数组中的每个对象作为单独的隐藏文档进行索引,这意味着可以使用nested查询独立于其他对象来查询每个嵌套对象:

PUT my_index
{
  "mappings": {
    "properties": {
      "user": {
        "type": "nested" 
      }
    }
  }
}

PUT my_index/_doc/1
{
  "group" : "fans",
  "user" : [
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

GET my_index/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "Smith" }} 
          ]
        }
      }
    }
  }
}

GET my_index/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "White" }} 
          ]
        }
      },
      "inner_hits": { 
        "highlight": {
          "fields": {
            "user.first": {}
          }
        }
      }
    }
  }
}

字段word被映射为nested类型,而不是object

这个查询不能匹配(该文档),因为AliceSmith不是在同一个嵌套对象里。

这个查询能匹配,因为AliceWhite是在同一个嵌套对象里。

inner_hits允许突出显示匹配的嵌套文档。

nested文档交互

嵌套文档可以:

因为嵌套文档是作为单独的文档进行索引的,所以只能在nested查询、nested/reverse_nested聚合或嵌套内部命中的范围内访问它们。

例如,如果嵌套文档中的字符串字段将index_options设置为offsets以允许在高亮阶段使用发布,则这些 offsets 在高亮阶段(主要阶段之一)将不可用。 相反,高亮需要通过嵌套内部命中来执行。 在通过docvalue_fieldsstored_fields进行搜索的过程中加载字段时,同样的注意事项也适用。

nested字段的参数

nested字段接受下列参数:

dynamic
(可选,字符串) 新属性(properties)是否应该被动态的添加到一个已有的嵌套对象中。 接受true(默认),falsestrict
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

为了说明这个设置是如何工作的,考虑在前面的示例映射中添加另一个名为commentsnested类型。 对于每个文档,它包含的usercomment对象的总数必须低于该限制。

有关防止映射爆炸的其他设置,请参考防止映射爆炸的设置