写点什么

Elasticsearch 聚合学习之三:范围限定

作者:程序员欣宸
  • 2022 年 9 月 15 日
    广东
  • 本文字数:4604 字

    阅读完需:约 15 分钟

Elasticsearch聚合学习之三:范围限定

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 在《Elasticsearch 聚合学习》系列的前面两篇文章中,我们熟悉了基本聚合操作,但这些操作都是面向索引中的全部数据(例如所有汽车销售记录一共有几种颜色的汽车),今天要学习的是如何对一定范围内的数据做聚合(例如以前是看所有汽车一共有几种颜色,现在只看福特汽车一共有几种颜色);

系列文章列表

  1. 《Elasticsearch聚合学习之一:基本操作》

  2. 《Elasticsearch聚合学习之二:区间聚合》

  3. 《Elasticsearch聚合学习之三:范围限定》;

  4. 《Elasticsearch聚合学习之四:结果排序》

环境信息

  • 以下是本次实战的环境信息,请确保您的 Elasticsearch 可以正常运行:


  1. 操作系统:Ubuntu 18.04.2 LTS

  2. JDK:1.8.0_191

  3. Elasticsearch:6.7.1

  4. Kibana:6.7.1


  • 实战用的数据依然是一些汽车销售的记录,在第一章有详细的导入步骤,请参考操作,导入后您的 es 中的数据如下图:

本章概要

  • 本篇聚焦查询范围限定,由以下内容构成:


  1. 不做限定时的默认范围;

  2. 最简单的查询范围

  3. 全局桶

  4. 使用过滤器

  5. 桶内使用过滤器

不做限定时的默认范围

  • 下面是个普通的聚合请求,将文档按照 color 字段聚合,由于没有做任何范围限定,因此查询的是所有文档:


GET /cars/transactions/_search{  "size":0,  "aggs":{   "popular_colors":{     "terms": {       "field": "color"     }   }   }}
复制代码


  • 下面请求带上了查询条件 match_all**,匹配所有文档,和前面不带查询条件的请求达到了同样效果:


<font color="color"><code class="language-shell">GET /cars/transactions/_search{  "size":0,  "query": {            ------查询条件    "match_all": {}     ------匹配所有文档  },   "aggs":{   "popular_colors":{     "terms": {       "field": "color"     }   }   }}</code></font>
复制代码

最简单的查询范围

  • 前面提出了一个问题:福特汽车一共分为几种颜色?这就是最简单的范围限定聚合(限定了汽车品牌),查询 DSL 如下:


<font color="color"><code class="language-shell">GET /cars/transactions/_search{  "size":0,  "query": {                ---范围限定的查询    "term": {               ---查询类型是精确匹配      "make": "ford"        ---查询条件是品牌为福特    }  },   "aggs":{                  ---聚合   "popular_colors":{       ---聚合字段名     "terms": {             ---桶类型       "field": "color"     ---匹配字段是color     }   }   }}</code></font>
复制代码


  • 返回结果如下,只有福特汽车的聚合数据:


<font color="color"><code class="language-shell">{  "took" : 7,  "timed_out" : false,  "_shards" : {    "total" : 5,    "successful" : 5,    "skipped" : 0,    "failed" : 0  },  "hits" : {    "total" : 2,    "max_score" : 0.0,    "hits" : [ ]  },  "aggregations" : {                       ---聚合结果    "popular_colors" : {                   ---聚合字段      "doc_count_error_upper_bound" : 0,      "sum_other_doc_count" : 0,      "buckets" : [                        ---这个数组的元素是所有的桶        {          "key" : "blue",                  ---color为blue的文档          "doc_count" : 1                  ---文档数为1        },        {          "key" : "green",                 ---color为blue的文档          "doc_count" : 1                  ---文档数为1        }      ]    }  }}</code></font>
复制代码

全局桶

  • 如果想对比福特汽车的销售额和所有汽车的销售额,可以通过全局桶对所有文档做聚合,关键字是 global,全局桶的聚合不受范围限定的影响:


<font color="color"><code class="language-shell">GET /cars/transactions/_search{  "size": 0,  "query": {           ---范围限定的查询    "term": {                ---查询类型是精确匹配      "make": "ford"         ---查询条件是品牌为福特    }  },  "aggs": {                  ---聚合    "ford_sales": {          ---聚合字段名      "sum": {               ---直接对范围内的所有文档执行metrics,类型是累加        "field": "price"     ---选择price字段的值进行累加      }    },    "all": {                 ---聚合字段名      "global": {},          ---全局桶关键字,表示忽略前面term查询的范围限定      "aggs": {              ---聚合        "all_sales": {       ---聚合字段名          "sum": {           ---直接对范围内的所有文档执行metrics,类型是累加            "field": "price" ---选择price字段的值进行累加          }        }      }    }  }}</code></font>
复制代码


  • 来看看结果:


<font color="color"><code class="language-shell">......  "aggregations" : {         ---聚合结果    "all" : {                ---全局桶的聚合结果(term查询无效)      "doc_count" : 8,       ---文档总数      "all_sales" : {        ---聚合字段名        "value" : 212000.0   ---总销售额      }    },    "ford_sales" : {         ---聚合字段名(term查询限定了范围,只有福特汽车的销售记录)      "value" : 55000.0      ---福特汽车销售额    }  }}</code></font>
复制代码

不止是 query

  • 前面的范围限定用到了 query,其实适用于查询的过滤器也能应用在聚合操作中,下面是过滤+聚合的查询,和前面一样,也是统计总销售和和福特汽车的销售额:


<font color="color"><code class="language-shell">GET /cars/transactions/_search{  "size": 0,  "query": {    "bool": {                 ---布尔查询,里面可以将query和filter组合使用      "filter": {             ---本例只用到了filter        "term": {             ---精确匹配          "make": "ford"      ---匹配福特品牌         }      }    }  },  "aggs": {                   ---聚合结果    "ford_sales": {           ---聚合字段名      "sum": {                ---metrics操作,累加        "field": "price"      ---累加字段是price      }    },    "all": {                  ---聚合字段名                        "global": {},           ---全局桶关键字,表示忽略范围限定      "aggs": {               ---聚合        "all_sales": {        ---聚合字段名          "sum": {            ---metrics操作,累加            "field": "price"  ---累加字段是price          }        }      }    }  }}</code></font>
复制代码


  • 查询结果如下,和 query 的一样:


<font color="color"><code class="language-shell">......  "aggregations" : {    "all" : {      "doc_count" : 8,      "all_sales" : {        "value" : 212000.0      }    },    "ford_sales" : {      "value" : 55000.0    }  }}</code></font>
复制代码


  • 注意:虽然 query 和 filter 限定范围的结果是一样的,但是 filter 会忽略评分,并且有可能缓存结果数据,这些都是性能上的优势;

桶内 filter

  • 学习桶内 filter 之前,先看看官方的布尔查询 DSL,如下所示,查询 JSON 对象的内部可以加入 filter,对查询结果做过滤:


<font color="color"><code class="language-shell">GET /_search{  "query": {     "bool": {       "must": [                                     ---布尔查询        { "match": { "title":   "Search"        }},         { "match": { "content": "Elasticsearch" }}        ],      "filter": [                                   ---对查询结果做过滤        { "term":  { "status": "published" }},         { "range": { "publish_date": { "gte": "2015-01-01" }}}       ]    }  }}</code></font>
复制代码


  • 桶内 filter 和布尔查询中的 filter 类似,对进入桶中的数据可以加入 filter,这样桶内的数据就是此 filter 过滤后的数据了;

  • 举个例子,统计蓝色的福特汽车销售额,首先限定品牌范围,这个可以直接用之前的限定方式,然后在桶内加入一个 filter,只保留颜色为蓝色的文档:


<font color="color"><code class="language-shell">GET /cars/transactions/_search{  "size": 0,  "query": {    "bool": {                 ---布尔查询,里面可以将query和filter组合使用      "filter": {             ---本例只用到了filter        "term": {             ---精确匹配          "make": "ford"      ---匹配福特品牌         }      }    }  },  "aggs": {    "sales": {      "filter": {             ---桶内filter        "term": {             ---精确匹配          "color": "blue"     ---匹配蓝色        }      },      "aggs": {        "blue_sales": {          "sum": {            ---metrics操作,累加            "field": "price"          }        }      }    }  }}</code></font>
复制代码


  • 返回结果如下,可见 hits.total 等于 2,表示查询到了两个文档,但是 sales.doc_count 等于 1,表示桶内 filter 作用后再桶内只剩下一个文档了:


<font color="color"><code class="language-shell">  "hits" : {    "total" : 2,    "max_score" : 0.0,    "hits" : [ ]  },  "aggregations" : {    "sales" : {      "doc_count" : 1,      "green_sales" : {        "value" : 25000.0      }    }  }}</code></font>
复制代码

后过滤器(post_filter)

  • 还有一种特殊的 filter,名为 post_filter,其作用描述如下:

  • 正常的聚合:先查询,得到查询结果 A,再用 A 做聚合操作得到结果 B,最后返回 B 和 A;

  • 带有 post_filter 的聚合:先查询,得到查询结果 A,再用 A 做聚合操作得到结果 B,然后用 A 做过滤得到 C(过滤条件就是 post_filter),最后返回 B 和 C;

  • 可见无论是否使用 post_filter,返回的聚合结果都是根据 A 生成的 B,不同之处在于用了 post_filter 就不返回 A,而是返回 A 的过滤结果;

  • 以下是来自《Elasticsearch 权威指南》的 post_filter 示例:


<font color="color"><code class="language-shell">GET /cars/transactions/_search{    "size" : 0,    "query": {        "match": {            "make": "ford"        }    },    "post_filter": {            "term" : {            "color" : "green"        }    },    "aggs" : {        "all_colors": {            "terms" : { "field" : "color" }        }    }}</code></font>
复制代码


  • 值得注意得是:如果只做查询不做聚合,post_filter 的作用和我们常用的 filter 是类似的,但由于 post_filter 是在查询之后才会执行,所以 post_filter 不具备 filter 对查询带来的好处(忽略评分、缓存等),因此,在普通的查询中不要用 post_filter 来替代 filter;

  • 如果您向进一步了解 post_filter,请参考《理解elasticsearch的post_filter》

  • 至此,带有范围限定的聚合操作实战就全部完成了,目前所有示例的结果都是默认排序的,接下来的章节将一起学习了解如何对聚合结果做排序。

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...


发布于: 刚刚阅读数: 3
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
Elasticsearch聚合学习之三:范围限定_elasticsearch_程序员欣宸_InfoQ写作社区