前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Elasticsearch使用:嵌套对象

Elasticsearch使用:嵌套对象

原创
作者头像
HLee
修改2021-10-13 17:52:09
6K0
修改2021-10-13 17:52:09
举报
文章被收录于专栏:房东的猫

简介

官网地址:https://www.elastic.co/guide/cn/elasticsearch/guide/current/nested-objects.html

由于在 Elasticsearch 中单个文档的增删改都是原子性操作,那么将相关实体数据都存储在同一文档中也就理所当然。 比如说,我们可以将订单及其明细数据存储在一个文档中。又比如,我们可以将一篇博客文章的评论以一个 comments 数组的形式和博客文章放在一起:

代码语言:javascript
复制
PUT /my_index/blogpost/1
{
  "title": "Nest eggs",
  "body":  "Making your money work...",
  "tags":  [ "cash", "shares" ],
  "comments": [   # 如果我们依赖字段自动映射,那么 comments 字段会自动映射为 object 类型。
    {
      "name":    "John Smith",
      "comment": "Great article",
      "age":     28,
      "stars":   4,
      "date":    "2014-09-01"
    },
    {
      "name":    "Alice White",
      "comment": "More like this please",
      "age":     31,
      "stars":   5,
      "date":    "2014-10-22"
    }
  ]
}

由于所有的信息都在一个文档中,当我们查询时就没有必要去联合文章和评论文档,查询效率就很高。

但是当我们使用如下查询时,上面的文档也会被当做是符合条件的结果:

代码语言:javascript
复制
GET /_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "Alice" }},
        { "match": { "age":  28      }}   # Alice实际是31岁,不是28!
      ]
    }
  }
}

正如我们在 对象数组 中讨论的一样,出现上面这种问题的原因是 JSON 格式的文档被处理成如下的扁平式键值对的结构。

代码语言:javascript
复制
{
  "title":            [ eggs, nest ],
  "body":             [ making, money, work, your ],
  "tags":             [ cash, shares ],
  "comments.name":    [ alice, john, smith, white ],
  "comments.comment": [ article, great, like, more, please, this ],
  "comments.age":     [ 28, 31 ],
  "comments.stars":   [ 4, 5 ],
  "comments.date":    [ 2014-09-01, 2014-10-22 ]
}

Alice 和 31 、 John2014-09-01 之间的相关性信息不再存在。虽然 object 类型 (参见 内部对象) 在存储 单一对象 时非常有用,但对于对象数组的搜索而言,毫无用处。

嵌套对象 就是来解决这个问题的。comments 字段类型设置为 nested 而不是 object 后,每一个嵌套对象都会被索引为一个 隐藏的独立文档 ,举例如下:

代码语言:javascript
复制
{ # 第一个 嵌套文档
  "comments.name":    [ john, smith ],  
  "comments.comment": [ article, great ],
  "comments.age":     [ 28 ],
  "comments.stars":   [ 4 ],
  "comments.date":    [ 2014-09-01 ]
}
{ #	第二个 嵌套文档
  "comments.name":    [ alice, white ],
  "comments.comment": [ like, more, please, this ],
  "comments.age":     [ 31 ],
  "comments.stars":   [ 5 ],
  "comments.date":    [ 2014-10-22 ]
}
{ # 根文档 或者也可称为父文档
  "title":            [ eggs, nest ],
  "body":             [ making, money, work, your ],
  "tags":             [ cash, shares ]
}

在独立索引每一个嵌套对象后,对象中每个字段的相关性得以保留。我们查询时,也仅仅返回那些真正符合条件的文档。

不仅如此,由于嵌套文档直接存储在文档内部,查询时嵌套文档和根文档联合成本很低,速度和单独存储几乎一样。

嵌套文档是隐藏存储的,我们不能直接获取。如果要增删改一个嵌套对象,我们必须把整个文档重新索引才可以。值得注意的是,查询的时候返回的是整个文档,而不是嵌套文档本身。

嵌套对象映射

设置一个字段为 nested 很简单 —  你只需要将字段类型 object 替换为 nested

代码语言:javascript
复制
PUT /my_index
{
  "mappings": {
    "blogpost": {
      "properties": {
        "comments": {
          "type": "nested",   # nested 字段类型的设置参数与 object 相同。
          "properties": {
            "name":    { "type": "string"  },
            "comment": { "type": "string"  },
            "age":     { "type": "short"   },
            "stars":   { "type": "short"   },
            "date":    { "type": "date"    }
          }
        }
      }
    }
  }
}

这就是需要设置的一切。至此,所有 comments 对象会被索引在独立的嵌套文档中。可以查看 nested 类型参考文档 获取更多详细信息。

嵌套对象查询

由于嵌套对象 被索引在独立隐藏的文档中,我们无法直接查询它们。 相应地,我们必须使用 nested 查询 去获取它们。

代码语言:javascript
复制
GET /my_index/blogpost/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "eggs"   # title 子句是查询根文档的。
          }
        },
        {
          "nested": {
            "path": "comments",   # nested 子句作用于嵌套字段 comments 。在此查询中,既不能查询根文档字段,也不能查询其他嵌套文档。
            "query": {
              "bool": {
                "must": [   # comments.name 和 comments.age 子句操作在同一个嵌套文档中。
                  {
                    "match": {
                      "comments.name": "john"
                    }
                  },
                  {
                    "match": {
                      "comments.age": 28
                    }
                  }
                ]
              }
            }
          }
        }
      ]
}}}

nested 字段可以包含其他的 nested 字段。同样地,nested 查询也可以包含其他的nested 查询。而嵌套的层次会按照你所期待的被应用。

nested 查询肯定可以匹配到多个嵌套的文档。每一个匹配的嵌套文档都有自己的相关度得分,但是这众多的分数最终需要汇聚为可供根文档使用的一个分数。

默认情况下,根文档的分数是这些嵌套文档分数的平均值。可以通过设置 score_mode 参数来控制这个得分策略,相关策略有 avg (平均值), max (最大值), sum (加和) 和 none (直接返回 1.0 常数值分数)。

代码语言:javascript
复制
GET /my_index/blogpost/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "eggs"
          }
        },
        {
          "nested": {
            "path": "comments",
            "score_mode": "max",   # 返回最优匹配嵌套文档的 _score 给根文档使用。
            "query": {
              "bool": {
                "must": [
                  {
                    "match": {
                      "comments.name": "john"
                    }
                  },
                  {
                    "match": {
                      "comments.age": 28
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}

如果 nested 查询放在一个布尔查询的 filter 子句中,其表现就像一个 nested 查询,只是score_mode 参数不再生效。因为它被用于不打分的查询中 — 只是符合或不符合条件,不必打分 — 那么score_mode 就没有任何意义,因为根本就没有要打分的地方。

使用嵌套字段排序

尽管嵌套字段的值存储于独立的嵌套文档中,但依然有方法按照嵌套字段的值排序。 让我们添加另一个记录,以使得结果更有意思:

代码语言:javascript
复制
PUT /my_index/blogpost/2
{
  "title": "Investment secrets",
  "body":  "What they don't tell you ...",
  "tags":  [ "shares", "equities" ],
  "comments": [
    {
      "name":    "Mary Brown",
      "comment": "Lies, lies, lies",
      "age":     42,
      "stars":   1,
      "date":    "2014-10-18"
    },
    {
      "name":    "John Smith",
      "comment": "You're making it up!",
      "age":     28,
      "stars":   2,
      "date":    "2014-10-16"
    }
  ]
}

假如我们想要查询在10月份收到评论的博客文章,并且按照 stars 数的最小值来由小到大排序,那么查询语句如下:

代码语言:javascript
复制
GET /_search
{
  "query": {
    "nested": {   # 此处的 nested 查询将结果限定为在10月份收到过评论的博客文章。
      "path": "comments",
      "filter": {
        "range": {
          "comments.date": {
            "gte": "2014-10-01",
            "lt":  "2014-11-01"
          }
        }
      }
    }
  },
  "sort": {
    "comments.stars": {   # 结果按照匹配的评论中 comment.stars 字段的最小值 (min) 来由小到大 (asc) 排序。
      "order": "asc",   # 结果按照匹配的评论中 comment.stars 字段的最小值 (min) 来由小到大 (asc) 排序。
      "mode":  "min",   # 结果按照匹配的评论中 comment.stars 字段的最小值 (min) 来由小到大 (asc) 排序。
      "nested_path": "comments",  # 排序子句中的 nested_path 和 nested_filter 和 query 子句中的 nested 查询相同,原因在下面有解释。
      "nested_filter": {
        "range": {
          "comments.date": {
            "gte": "2014-10-01",
            "lt":  "2014-11-01"
          }
        }
      }
    }
  }
}

我们为什么要用 nested_path 和 nested_filter 重复查询条件呢?原因在于,排序发生在查询执行之后。 查询条件限定了在10月份收到评论的博客文档,但返回的是博客文档。如果我们不在排序子句中加入 nested_filter , 那么我们对博客文档的排序将基于博客文档的所有评论,而不是仅仅在10月份接收到的评论。

嵌套聚合

在查询的时候,我们使用 nested 查询就可以获取嵌套对象的信息。同理, nested 聚合允许我们对嵌套对象里的字段进行聚合操作。

代码语言:javascript
复制
GET /my_index/blogpost/_search
{
  "size" : 0,
  "aggs": {
    "comments": {   # nested 聚合 “进入” 嵌套的 comments 对象。
      "nested": {
        "path": "comments"
      },
      "aggs": {
        "by_month": {
          "date_histogram": {  # comment对象根据 comments.date 字段的月份值被分到不同的桶。
            "field":    "comments.date",
            "interval": "month",
            "format":   "yyyy-MM"
          },
          "aggs": {
            "avg_stars": { # 计算每个桶内star的平均数量。
              "avg": { 
                "field": "comments.stars"
              }
            }
          }
        }
      }
    }
  }
}

从下面的结果可以看出聚合是在嵌套文档层面进行的:

代码语言:javascript
复制
...
"aggregations": {
  "comments": {
     "doc_count": 4, 
     "by_month": {
        "buckets": [
           {
              "key_as_string": "2014-09",
              "key": 1409529600000,
              "doc_count": 1,   # 总共有4个 comments 对象 :1个对象在9月的桶里,3个对象在10月的桶里。
              "avg_stars": {
                 "value": 4
              }
           },
           {
              "key_as_string": "2014-10",
              "key": 1412121600000,
              "doc_count": 3,   # 总共有4个 comments 对象 :1个对象在9月的桶里,3个对象在10月的桶里。
              "avg_stars": {
                 "value": 2.6666666666666665
              }
           }
        ]
     }
  }
}
...

逆向嵌套聚合

nested 聚合 只能对嵌套文档的字段进行操作。 根文档或者其他嵌套文档的字段对它是不可见的。 然而,通过 reverse_nested 聚合,我们可以 走出 嵌套层级,回到父级文档进行操作。

例如,我们要基于评论者的年龄找出评论者感兴趣 tags 的分布。 comment.age 是一个嵌套字段,但 tags 在根文档中:

代码语言:javascript
复制
GET /my_index/blogpost/_search
{
  "size" : 0,
  "aggs": {
    "comments": {
      "nested": {   # nested 聚合进入 comments 对象。
        "path": "comments"
      },
      "aggs": {
        "age_group": {
          "histogram": {  # histogram 聚合基于 comments.age 做分组,每10年一个分组。 
            "field":    "comments.age",
            "interval": 10
          },
          "aggs": {
            "blogposts": {
              "reverse_nested": {},   # reverse_nested 聚合退回根文档。
              "aggs": {
                "tags": {
                  "terms": {   # terms 聚合计算每个分组年龄段的评论者最常用的标签词。
                    "field": "tags"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

简略结果如下所示:

代码语言:javascript
复制
..
"aggregations": {
  "comments": {
     "doc_count": 4,   # 一共有4条评论。
     "age_group": {
        "buckets": [
           {
              "key": 20, # 在20岁到30岁之间总共有两条评论。
              "doc_count": 2,  # 在20岁到30岁之间总共有两条评论。
              "blogposts": {
                 "doc_count": 2, # 这些评论包含在两篇博客文章中。
                 "tags": {
                    "doc_count_error_upper_bound": 0,
                    "buckets": [   # 在这些博客文章中最热门的标签是 shares、 cash、equities。
                       { "key": "shares",   "doc_count": 2 },
                       { "key": "cash",     "doc_count": 1 },
                       { "key": "equities", "doc_count": 1 }
                    ]
                 }
              }
           },
...

嵌套对象的使用时机

嵌套对象在只有一个主要实体时非常有用,这个主要实体包含有限个紧密关联但又不是很重要的实体,例如我们的blogpost 对象包含评论对象。 在基于评论的内容查找博客文章时,nested 查询有很大的用处,并且可以提供更快的查询效率。

嵌套模型的缺点如下:

  • 当对嵌套文档做增加、修改或者删除时,整个文档都要重新被索引。嵌套文档越多,这带来的成本就越大。
  • 查询结果返回的是整个文档,而不仅仅是匹配的嵌套文档。尽管目前有计划支持只返回根文档中最佳匹配的嵌套文档,但目前还不支持。

有时你需要在主文档和其关联实体之间做一个完整的隔离设计。这个隔离是由 父子关联 提供的。

实例分享

image
image

嵌套文档看似与文档内有一个集合字段类似,但是实则有很大区别,以上面图中嵌套文档为例,留言1,留言2,留言3虽然都在当前文章所在的文档内,但是在内部其实存储为4个独立文档,如下图所示。

image
image

同时,嵌套文档的字段类型需要设置为nested,设置成nested后的不能被直接查询,需要使用nested查询。

新建索引

代码语言:javascript
复制
{
  "title": "这是一篇文章",
  "body":  "这是一篇文章,从哪里说起呢? ... ...",
  "comments": [ 
    {
      "name":    "张三",
      "comment": "写的不错",
      "age":     28,
      "date":    "2020-05-04"
    },
    {
      "name":    "李四",
      "comment": "写的很好",
      "age":     20,
      "date":    "2020-05-04"
    },
    {
      "name":    "王五",
      "comment": "这是一篇非常棒的文章",
      "age":     31,
      "date":    "2020-05-01"
    }
  ]
}

创建索引名和type均为blog的索引,其中comments字段为嵌套文档类型,需要将type设置为nested,其余都是一些正常的字段
代码语言:javascript
复制
设置Setting:

PUT /test_book
{
  "settings": {
    "number_of_shards": 1,
    "analysis": {
      "analyzer": {
        "index_ansj_analyzer": {
          "type": "custom",
          "tokenizer": "index_ansj",
          "filter": [
            "my_synonym",
            "asciifolding"
          ]
        },
        "comma": {
          "type": "pattern",
          "pattern": ","
        },
        "shingle_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "shingle_filter"
          ]
        }
      },
      "filter": {
        "my_synonym": {
          "type": "synonym",
          "synonyms_path": "analysis/synonym.txt"
        },
        "shingle_filter": {
          "type": "shingle",
          "min_shingle_size": 2,
          "max_shingle_size": 2,
          "output_unigrams": false
        }
      }
    }
  }
} 

设置Mapping:

PUT /test_book/_mapping/_doc
{
    "_doc":{
        "properties":{
            "comments":{
                "type":"nested",
                "properties":{
                    "date":{
                        "type":"date"
                    },
                    "name":{
                        "type":"text",
                        "fields":{
                            "keyword":{
                                "type":"keyword"
                            }
                        }
                    },
                    "comment":{
                        "type":"text",
                        "fields":{
                            "keyword":{
                                "type":"keyword"
                            }
                        }
                    },
                    "age":{
                        "type":"long"
                    }
                }
            },
            "body":{
                "type":"text",
                "fields":{
                    "keyword":{
                        "type":"keyword"
                    }
                }
            },
            "title":{
                "type":"text",
                "fields":{
                    "keyword":{
                        "type":"keyword"
                    }
                }
            }
        }
    }
}

索引数据

代码语言:javascript
复制
PUT test_book/_doc/1
{
    "title":"这是一篇文章",
    "body":"这是一篇文章,从哪里说起呢? ... ...",
    "comments":[
        {
            "name":"张三",
            "comment":"写的不错",
            "age":28,
            "date":"2020-05-04"
        },
        {
            "name":"李四",
            "comment":"写的很好",
            "age":20,
            "date":"2020-05-04"
        },
        {
            "name":"王五",
            "comment":"这是一篇非常棒的文章",
            "age":31,
            "date":"2020-05-01"
        }
    ]
}

PUT test_book/_doc/2
{
  "title": "这是一篇文章2",
  "body":  "这是一篇文章2,从哪里说起呢? ... ...",
  "comments": [ 
    {
      "name":    "张三",
      "comment": "写的不错",
      "age":     28,
      "date":    "2020-05-11"
    },
    {
      "name":    "李四",
      "comment": "写的很好",
      "age":     20,
      "date":    "2020-05-16"
    },
    {
      "name":    "王五",
      "comment": "这是一篇非常棒的文章",
      "age":     31,
      "date":    "2020-05-01"
    }
  ]
}

PUT test_book/_doc/3
{
  "title": "这是一篇文章3",
  "body":  "这是一篇文章3,从哪里说起呢? ... ...",
  "comments": [ 
    {
      "name":    "张三",
      "comment": "写的不错",
      "age":     28,
      "date":    "2020-05-03"
    },
    {
      "name":    "李四",
      "comment": "写的很好",
      "age":     20,
      "date":    "2020-05-20"
    },
    {
      "name":    "王五",
      "comment": "这是一篇非常棒的文章",
      "age":     31,
      "date":    "2020-05-01"
    }
  ]
}

POST test_book/_search
{
  "query": {
    "match_all": {}
  }
}

结果:
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "test_book",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "title" : "这是一篇文章",
          "body" : "这是一篇文章,从哪里说起呢? ... ...",
          "comments" : [
            {
              "name" : "张三",
              "comment" : "写的不错",
              "age" : 28,
              "date" : "2020-05-04"
            },
            {
              "name" : "李四",
              "comment" : "写的很好",
              "age" : 20,
              "date" : "2020-05-04"
            },
            {
              "name" : "王五",
              "comment" : "这是一篇非常棒的文章",
              "age" : 31,
              "date" : "2020-05-01"
            }
          ]
        }
      }
    ]
  }
}

查询数据

代码语言:javascript
复制
直接查询根文档:

POST test_book/_search
{
  "query": {
    "match": {
      "title.keyword": "这是一篇文章"
    }
  }
}

查询结果:
{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "test_book",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.2876821,
        "_source" : {
          "title" : "这是一篇文章",
          "body" : "这是一篇文章,从哪里说起呢? ... ...",
          "comments" : [
            {
              "name" : "张三",
              "comment" : "写的不错",
              "age" : 28,
              "date" : "2020-05-04"
            },
            {
              "name" : "李四",
              "comment" : "写的很好",
              "age" : 20,
              "date" : "2020-05-04"
            },
            {
              "name" : "王五",
              "comment" : "这是一篇非常棒的文章",
              "age" : 31,
              "date" : "2020-05-01"
            }
          ]
        }
      }
    ]
  }
}

备注:要是想直接查询内部文档是查询不到的,需要加上nested即可

POST test_book/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "文章"
          }
        },
        {
          "nested": {
            "path": "comments",
            "query": {
              "bool": {
                "must": [
                  {
                    "match": {
                      "comments.name": "张三"
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}

查询结果:
{
  "took" : 10,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 2.5370226,
    "hits" : [
      {
        "_index" : "test_book",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 2.5370226,
        "_source" : {
          "title" : "这是一篇文章",
          "body" : "这是一篇文章,从哪里说起呢? ... ...",
          "comments" : [
            {
              "name" : "张三",
              "comment" : "写的不错",
              "age" : 28,
              "date" : "2020-05-04"
            },
            {
              "name" : "李四",
              "comment" : "写的很好",
              "age" : 20,
              "date" : "2020-05-04"
            },
            {
              "name" : "王五",
              "comment" : "这是一篇非常棒的文章",
              "age" : 31,
              "date" : "2020-05-01"
            }
          ]
        }
      }
    ]
  }
}

备注:nested中查询的是嵌套文档的内容,语法与正常查询时一致。 nested 查询肯定可以匹配到多个嵌套的文档。每一个匹配的嵌套文档都有自己的相关度得分,但是这众多的分数最终需要汇聚为可供根文档使用的一个分数。 默认情况下,根文档的分数是这些嵌套文档分数的平均值。可以通过设置 score_mode 参数来控制这个得分策略,相关策略有 avg(平均值),max(最大值),sum(加和) 和 none(直接返回 1.0 常数值分数)。

排序

代码语言:javascript
复制
POST test_book/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "文章"
          }
        },
        {
          "nested": {
            "path": "comments",
            "query": {
              "bool": {
                "must": [
                  {
                    "match": {
                      "comments.name": "张三"
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  },
  "sort": {
    "comments.date": {
      "order": "desc",
      "mode": "max",
      "nested_path": "comments",
      "nested_filter": {
        "bool": {
          "must": [
            {
              "match": {
                "comments.name": "张三"
              }
            }
          ]
        }
      }
    }
  }
}

备注:需要注意的是,在sort内,又添加了nested_filter来过滤一遍上面嵌套文档的查询条件,原因是这样的,在嵌套文档查询排序时是先按照条件进行查询,查询后再进行排序,那么可能由于数据的原因,导致排序的字段不是按照匹配上的数据进行排序

聚合

聚合的场景可能也比较常见,其实熟悉上面嵌套文档的使用的话,对聚合文档使用难度应该也不大。

代码语言:javascript
复制
PUT test_book/_doc/4
{
  "title": "这是一篇文章4",
  "body":  "这是一篇文章4,从哪里说起呢? ... ...",
  "comments": [ 
    {
      "name":    "张三",
      "comment": "写的不错",
      "age":     28,
      "date":    "2020-03-03"
    },
    {
      "name":    "李四",
      "comment": "写的很好",
      "age":     20,
      "date":    "2020-04-20"
    },
    {
      "name":    "王五",
      "comment": "这是一篇非常棒的文章",
      "age":     31,
      "date":    "2020-06-01"
    }
  ]
}

举例:需要查询每个月评论人数的平均数,查询语句如下:

代码语言:javascript
复制
POST test_book/_search
{
  "size": 0,
  "aggs": {
    "comments": {
      "nested": {
        "path": "comments"
      },
      "aggs": {
        "by_month": {
          "date_histogram": {
            "field": "comments.date",
            "interval": "month",
            "format": "yyyy-MM"
          },
          "aggs": {
            "avg_stars": {
              "avg": {
                "field": "comments.age"
              }
            }
          }
        }
      }
    }
  }
}

查询结果:
{
  "took" : 16,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 4,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "comments" : {
      "doc_count" : 12,
      "by_month" : {
        "buckets" : [
          {
            "key_as_string" : "2020-03",
            "key" : 1583020800000,
            "doc_count" : 1,
            "avg_stars" : {
              "value" : 28.0
            }
          },
          {
            "key_as_string" : "2020-04",
            "key" : 1585699200000,
            "doc_count" : 1,
            "avg_stars" : {
              "value" : 20.0
            }
          },
          {
            "key_as_string" : "2020-05",
            "key" : 1588291200000,
            "doc_count" : 9,
            "avg_stars" : {
              "value" : 26.333333333333332
            }
          },
          {
            "key_as_string" : "2020-06",
            "key" : 1590969600000,
            "doc_count" : 1,
            "avg_stars" : {
              "value" : 31.0
            }
          }
        ]
      }
    }
  }
}

备注:正如本文所说,嵌套文档中,所有内容都在同一个文档内,这就导致嵌套文档进行增加、修改或者删除时,整个文档都要重新被索引。嵌套文档越多,这带来的成本就越大。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 嵌套对象映射
  • 嵌套对象查询
  • 使用嵌套字段排序
  • 嵌套聚合
    • 逆向嵌套聚合
      • 嵌套对象的使用时机
      • 实例分享
        • 新建索引
          • 索引数据
            • 查询数据
              • 排序
                • 聚合
                相关产品与服务
                Elasticsearch Service
                腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档