Elasticsearch 聚合学习之四:结果排序

语言: CN / TW / HK

欢迎访问我的 GitHub

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

本篇概览

  • 本文是《Elasticsearch 聚合学习》系列的第四篇,在前面的实战中,聚合的结果以桶(bucket)为单位,放在 JSON 数组中返回,这些数据是没有排序的,今天来学习如何给这些数据进行排序;

系列文章列表

  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 中的数据如下图:

  • 接下来一起实战聚合排序吧;

默认排序

  • 之前文章中的聚合查询,我们都没有做排序设置,此时 es 会用每个桶的 doc_count 字段做降序,下图是个 terms 桶聚合的示例,可见返回了三个 bucket 对象,是按照 doc_count 字段降序排列的:

内置排序

  • 除了自定义排序,es 自身也内置了两种排序参数,可以直接拿来使用:

  • _count:这个参数对应的就是 doc_count,以下请求的排序效果和默认的排序效果是一致的:

GET /cars/transactions/_search{  "size":0,  "aggs":{   "popular_colors":{     "terms": {       "field": "color",       "order": {             ---表示要对聚合结果做排序         "_count": "desc"     ---排序字段是doc_count,顺序是降序       }     }   }   }}

复制代码

  • _key:在区间聚合的时候(histogram 或者 date_histogram),可以根据桶的 key 做排序:

GET /cars/transactions/_search{  "size": 0,  "aggs": {    "price": {      "histogram": {           ---区间聚合        "field": "price",      ---取price字段的值        "interval": 20000,     ---每个区间的大小是20000        "order": {             ---表示要对聚合结果做排序          "_key": "desc"       ---排序字段是桶的key值,这里是每个区间的起始值,顺序是降序        }      }    }  }}

复制代码

  • 返回结果如下,已经按照 key 的大小从大到小排序:

  ......  "aggregations" : {    "price" : {      "buckets" : [        {          "key" : 80000.0,          "doc_count" : 1        },        {          "key" : 60000.0,          "doc_count" : 0        },        {          "key" : 40000.0,          "doc_count" : 0        },        {          "key" : 20000.0,          "doc_count" : 4        },        {          "key" : 0.0,          "doc_count" : 3        }      ]    }  }}

复制代码

  • 但是在实际操作中发现,6.7.1 版本中,除了 histogram 和 date_histogram,terms 桶也可以用**_key**排序,如下图,是按照 key 的字母降序:

  • desc 改为 asc 之后返回如下图,变成了按照 key 的首字母升序排序:

  • 另外 《Elasticsearch 权威指南》 中还提到一种内置排序类型**_term**,但是 《Elasticsearch官方文档》 中宣布该类型在 6.0 之后已经废弃,如下:

  • 也许是"手贱"的缘故,我还是用_term 试了下,可以返回结果,但是会建议用_key 替代_term,如下图:

按照 metrics 排序(metrics 结果只有一个值)

  • 常见的 metrics 有累加和(sum)、最大值(max)、最小值(min)、平均值(avg),这些 metrics 的特点是处理结果只有一个值,我们可以按照这个结果来排序,例如计算每个汽车品牌的销售额,再按照销售额排序:

GET /cars/transactions/_search{  "size": 0,  "aggs": {    "sales_rank": {      "terms": {               ---桶类型是terms        "field": "make",       ---按照make字段聚合        "order": {             ---要求排序          "sales": "desc"      ---排序字段是sales        }      },      "aggs": {        "sales": {            ---metrics处理后的结果保存在名为sales的字段中,排序已经指定了该字段          "sum": {            ---桶内的metrics处理,类型是累加            "field": "price"  ---将price字段的值累加          }        }      }    }  }}

复制代码

  • 下面是聚合结果,可见已按照每个品牌的销售额大小做了降序的排序:

......  "aggregations" : {                          ---聚合结果    "sales_rank" : {                          ---桶名称      "doc_count_error_upper_bound" : 0,      "sum_other_doc_count" : 0,      "buckets" : [                           ---这个JSON数组内是按照品牌聚合而成的所有桶        {          "key" : "bmw",                      ---品牌为bmw的桶          "doc_count" : 1,                    ---文档数量为1          "sales" : {                         ---metrics处理结果            "value" : 80000.0                 ---品牌为bmw的汽车销售总额是80000          }        },        {          "key" : "ford",          "doc_count" : 2,          "sales" : {            "value" : 55000.0          }        },        {          "key" : "honda",          "doc_count" : 3,          "sales" : {            "value" : 50000.0          }        },        {          "key" : "toyota",          "doc_count" : 2,          "sales" : {            "value" : 27000.0          }        }      ]    }  }}

复制代码

按照 metrics 排序(metrics 结果有多个值)

  • 和 sum、max 这些只有一个结果的 metrics 不同,extended_stats 的结果包含了数量、最大值、最小值、平均值、累加和等多种处理,此时必须要指定用其中的哪一项(否则会返回错误:Invalid aggregation order path [xxxx]. When ordering on a multi-value metrics aggregation a metric name must be specified):

GET /cars/transactions/_search{  "size": 0,  "aggs": {    "sales_rank": {      "terms": {                 ---桶类型是terms        "field": "make",         ---按照make字段聚合        "order": {               ---要求排序          "stat.avg": "asc"      ---排序字段是metrics结果的一个子项(平均值),升序        }      },      "aggs": {        "stat": {                ---metrics处理后的结果保存在名为stat的字段中,排序已经指定了该字段的agv子项(平均值)          "extended_stats": {    ---桶内的metrics处理,类型是计算数量、最大值、最小值、平均值等多个指标项            "field": "price"     ---将price字段的值拿来做metrics处理          }        }      }    }  }}

复制代码

  • 返回结果如下,可见已经按照 metrics 结果的 avg 子项做了升序排序:

......"aggregations" : {    "sales_rank" : {      "doc_count_error_upper_bound" : 0,      "sum_other_doc_count" : 0,      "buckets" : [        {          "key" : "toyota",          "doc_count" : 2,          "stat" : {            "count" : 2,            "min" : 12000.0,            "max" : 15000.0,            "avg" : 13500.0,               ---排序字段            "sum" : 27000.0,            "sum_of_squares" : 3.69E8,            "variance" : 2250000.0,            "std_deviation" : 1500.0,            "std_deviation_bounds" : {              "upper" : 16500.0,              "lower" : 10500.0            }          }        },        {          "key" : "honda",          "doc_count" : 3,          "stat" : {            "count" : 3,            "min" : 10000.0,            "max" : 20000.0,            "avg" : 16666.666666666668,    ---排序字段            "sum" : 50000.0,            "sum_of_squares" : 9.0E8,            "variance" : 2.222222222222221E7,            "std_deviation" : 4714.045207910315,            "std_deviation_bounds" : {              "upper" : 26094.757082487296,              "lower" : 7238.5762508460375            }          }        },        ......

复制代码

嵌套桶排序

  • 在聚合查询中,经常对聚合的数据再次做聚合处理,例如统计每个汽车品牌下的每种颜色汽车的销售额,这时候 DSL 中就有了多层 aggs 对象的嵌套,这就是嵌套桶(此名称来自 《Elasticsearch 权威指南》 ),如下图所示:

欢迎关注 InfoQ:程序员欣宸

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