Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Elasticsearch使用:嵌套对象

Elasticsearch使用:嵌套对象

原创
作者头像
HLee
修改于 2021-10-13 09:52:09
修改于 2021-10-13 09:52:09
6.3K00
代码可运行
举报
文章被收录于专栏:房东的猫房东的猫
运行总次数:0
代码可运行

简介

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
GET /_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "Alice" }},
        { "match": { "age":  28      }}   # Alice实际是31,不是28!
      ]
    }
  }
}

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "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
代码运行次数:0
运行
AI代码解释
复制
{ # 第一个 嵌套文档
  "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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
...
"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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
..
"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 查询有很大的用处,并且可以提供更快的查询效率。

嵌套模型的缺点如下:

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

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

实例分享

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

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

新建索引

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "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
代码运行次数:0
运行
AI代码解释
复制
设置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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
直接查询根文档:

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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Elasticsearch学习-嵌套文档
最近一段时间都在搞Elasticsearch搜索相关的工作,总结一下搜索知识点供大家参考。
dalaoyang
2020/05/17
1.1K0
白话Elasticsearch59-数据建模实战_ Nested Aggregation/ Reverse nested Aggregation对嵌套的博客评论数据进行聚合分析
基于白话Elasticsearch58-数据建模实战_基于nested object实现博客与评论嵌套关系的数据
小小工匠
2021/08/17
4510
Elasticsearch学习-父子文档
上一篇文章介绍了Elasticsearch的嵌套文档,这一篇来介绍另外一种关系文档,父子文档。
dalaoyang
2020/05/17
1.8K0
Elasticsearch使用:父-子关系文档(下)
父子文档在理解上来说,可以理解为一个关联查询,有些类似MySQL中的JOIN查询,通过某个字段关系来关联。父子文档与嵌套文档主要的区别在于,父子文档的父对象和子对象都是独立的文档,而嵌套文档中都在同一个文档中存储。如下图所示:
HLee
2021/04/25
1.9K0
Elasticsearch使用:父-子关系文档(下)
白话Elasticsearch58-数据建模实战_基于nested object实现博客与评论嵌套关系
所以,直接命中了这个document,name=黄药师,age=28,在范围之内,正好符合,所以被查询出来了。
小小工匠
2021/08/17
2280
触类旁通Elasticsearch:关联
ES本身不支持SQL数据库的join操作,在ES中定义关系的方法有对象类型、嵌套文档、父子关系和反规范化。
用户1148526
2019/05/25
6.3K0
干货 | Elasticsearch Nested类型深入详解
本文通过一个例子将Nested类型适合解决的问题、应用场景、使用方法串起来, 文中所有的DSL都在Elasticsearch6.X+验证通过。
铭毅天下
2018/10/24
4.5K0
干货 | Elasticsearch Nested类型深入详解
Elasticsearch之nested object
假设我们有这么一篇文章,并且存有两个评论 PUT 54288.top:9200/website/blogs/1 { "title": "花无缺发表的一篇帖子", "content": "我是花无缺,大家要不要考虑一下投资房产和买股票的事情啊。。。", "tags": [ "投资", "理财" ], "comments": [ { "name": "小鱼儿", "comment": "什么股票啊?推荐一下呗", "age": 28, "stars": 4, "date": "2016
丁D
2022/08/12
2700
触类旁通Elasticsearch:聚合
熟悉SQL的用户一定对聚合不会陌生,简单说任何应用于group by的查询都会执行一个聚合操作。ES的聚合(aggregation)加载和搜索相匹配的文档,并且完成各种统计型的计算。
用户1148526
2019/05/25
3.1K0
开始使用Elasticsearch (3)
在今天的这篇文章中,我们将来学习如何运用 Elasticsearch 来对我们的数据进行分析及一些关于 Analyzer 的介绍。在学习这个之前,我们必须完成之前的练习:
腾讯云大数据
2020/07/10
1.7K0
开始使用Elasticsearch (3)
《ElasticSearch6.x实战教程》之父-子关系文档
在非关系型数据库数据库中,我们常常会有表与表的关联查询。例如学生表和成绩表的关联查询就能查出学会的信息和成绩信息。在ES中,父子关系文档就类似于表的关联查询。
用户1148394
2019/07/22
9800
ElasticSearch 6.x 学习笔记:22.桶聚合
https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-aggregations-bucket.html 在页面右下角可以看到各类具体的Bucket聚合连接
程裕强
2022/05/06
8840
ElasticSearch 6.x 学习笔记:22.桶聚合
Elasticsearch使用:Aggregation API
聚合框架有助于基于搜索查询提供聚合数据。它基于称为聚合的简单构建块,可以组合以构建复杂的数据摘要。
HLee
2021/01/25
1.7K0
Elasticsearch使用:Aggregation API
【腾讯云ES】Elasticsearch Aggregations数据统计实践
Elasticsearch作为分布式搜索引擎,可支持各种数据类型(结构化/非结构化文本、数值等)的存储和快速查询,具有良好的可扩展性,可以支持不断增长的数据量。Elasticsearch不仅可以进行多种场景的数据查询,还提供了强大的聚合查询功能,可实现各种复杂的数据分析需求。 下面重点介绍ES中常用的聚合查询方法,并以系统中具体的功能实现为例,进行详细说明。
用户4591993
2022/12/09
1.5K0
Elasticsearch的R在这里,查询与聚合
term是用于精确匹配的,类似于sql语句中的“=”,因为“name”字段用的是standard默认分词器,其会将“张三”分成“张”和“三”,并不会匹配姓名为“张三”的人,而name.keyword可以让其不会进行分词。
Liusy
2020/09/01
3.2K0
Elasticsearch的R在这里,查询与聚合
Elasticsearch 数据搜索篇 转
这种方式会把查询的内容放入body中,会造成一定的开销,但是易于理解。在平时的练习中,推荐这种方式。
双面人
2019/04/10
6200
Elasticsearch 数据搜索篇
                                                                            转
Elasticsearch:透彻理解 Elasticsearch 中的 Bucket aggregation
Elasticsearch 除了在搜索方面非常之快,对数据分析也是非常重要的一面。正确理解 Bucket aggregation 对我们使用 Kibana 非常重要。Elasticsearch 提供了非常多的 aggregation 可以供我们使用。其中 Bucket aggregation 对于初学者来说也是比较不容易理解的一个。在今天的这篇文章中,我来重点讲述这个。
腾讯云大数据
2020/10/13
2.7K0
Elasticsearch:透彻理解 Elasticsearch 中的 Bucket aggregation
蛋疼的ElasticSearch(四)之基本用法、高级查询
ElasticSearch系列文章: 1.蛋疼的ElasticSearch(一)之安装ElasticSearch 2.蛋疼的ElasticSearch(二)之配置ElasticSearch Head插件 3.蛋疼的ElasticSearch(三)之配置elasticsearch-analysis-ik和集群
用户2032165
2018/12/04
5940
蛋疼的ElasticSearch(四)之基本用法、高级查询
乐优项目:Elasticsearch介绍和安装及使用-(六)
而商品的数量非常多,而且分类繁杂。如何能正确的显示出用户想要的商品,并进行合理的过滤,尽快促成交易,是搜索系统要研究的核心。
用户4396583
2024/08/14
3660
ElasticSearch 高级操作
在 Postman 中,向 ES 服务器发 DELETE 请求:http://127.0.0.1:9200/student
用户9615083
2022/12/25
7380
ElasticSearch  高级操作
推荐阅读
相关推荐
Elasticsearch学习-嵌套文档
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验