一 高阶概念
桶(Buckets)
满足特定条件的文档的集合
桶 简单来说就是满足特定条件的文档的集合:
一个雇员属于 男性 桶或者 女性 桶
奥尔巴尼属于 纽约 桶
日期2014-10-28属于 十月 桶
例如,浦东会被放入上海这个桶,而 整个上海桶会被放入中国这个桶。
elasticsearch 有很多种类型的桶,能让你通过很多种方式来划分文档(时间、最受欢迎的词、年龄区间、地理位置等等)。其实根本上都是通过同样的原理进行操作:基于条件来划分文档
指标(Metrics)
对桶内的文档进行统计计算
桶能让我们划分文档到有意义的集合,但是最终我们需要的是对这些桶内的文档进行一些指标的计算。分桶是一种达到目的的手段:它提供了一种给文档分组的方法来让我们可以计算感兴趣的指标。
大多数 指标 是简单的数学运算(例如最小值、平均值、最大值,还有汇总),这些是通过文档的值来计算。在实践中,指标能让你计算像平均薪资、最高出售价格、95%的查询延迟这样的数据
形象化一点就是SQL的count(),SUM(),MAX()等统计方法相当于指标,而GROUP BY color 相当于桶
聚合(aggregation)
聚合 是由桶和指标组成的。 聚合可能只有一个桶,可能只有一个指标,或者可能两个都有。也有可能有一些桶嵌套在其他桶里面。例如,我们可以通过所属国家来划分文档(桶),然后计算每个国家的平均薪酬(指标)
由于桶可以被嵌套,我们可以实现非常多并且非常复杂的聚合:
1.通过国家划分文档(桶)
2.然后通过性别划分每个国家(桶)
3.然后通过年龄区间划分每种性别(桶)
4.最后,为每个年龄区间计算平均薪酬(指标)
最后将告诉你每个 <国家, 性别, 年龄> 组合的平均薪酬。所有的这些都在一个请求内完成并且只遍历一次数据!
光说不练假把式,所以让我们先看一个例子。我们将会创建一些对家电经销商有用的聚合,数据是关家电交易的信息:颜色、制造商、售价、何时被出售等
准备数据:
`PUT /tvs
{
"mappings": {
"properties": {
"price": {
"type": "long"
},
"color": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"sold_date": {
"type": "date"
}
}
}
}
#添加数据
POST /tvs/_bulk
{"index":{}}
{"price":1000,"color":"红色","brand":"长虹","sold_date":"2016-10-28"}
{"index":{}}
{"price":2000,"color":"红色","brand":"长虹","sold_date":"2016-11-05"}
{"index":{}}
{"price":3000,"color":"绿色","brand":"小米","sold_date":"2016-05-18"}
{"index":{}}
{"price":1500,"color":"蓝色","brand":"TCL","sold_date":"2016-07-02"}
{"index":{}}
{"price":1200,"color":"绿色","brand":"TCL","sold_date":"2016-08-19"}
{"index":{}}
{"price":2000,"color":"红色","brand":"长虹","sold_date":"2016-11-05"}
{"index":{}}
{"price":8000,"color":"红色","brand":"三星","sold_date":"2017-01-01"}
{"index":{}}
{"price":2500,"color":"蓝色","brand":"小米","sold_date":"2017-02-12"}
{"index":{}}
{"price":1999,"color":"红色","brand":"长虹","sold_date":"2021-10-28"}
按颜色分组统计电视机销量
GET /tvs/_search
{
"size": 0,
"aggs": {
"love_colors": {
"terms": {
"field": "color"
}
}
}
}
size:0代表只获取聚合结果,而不要执行聚合的原始数据,结果集里面的hits.hits是[]
aggs:固定语法,要对一份数据执行分组聚合操作
popular_colors:就是对每个aggs,都要起一个名字,这个名字是随机的,你随便取什么都ok
terms:根据字段的值进行分组
field:根据指定的字段的值进行分组
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 8,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ] # 注意这里是空 因为size设置为零了
},
"aggregations" : {
"love_colors" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "红色",
"doc_count" : 4
},
{
"key" : "绿色",
"doc_count" : 2
},
{
"key" : "蓝色",
"doc_count" : 2
}
]
}
}
}
hits.hits:指定size=0,所以hits.hits就是空的,否则会把执行聚合的那些原始数据返回回来
aggregations:聚合结果
love_colors: 指定某个聚合的名称
buckets:指定的field划分出的buckets
key:每个bucket对应的那个值
doc_count: 每个bucket分组内,有多少个数据,每种颜色对应的bucket中的数据的
默认的排序规则:doc_count 降序排序
按颜色分组,平均avg价格
{
"size" : 0,
"aggs": {
"colors": {
"terms": {
"field": "color"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
其结果集最终会变成如下:
{
"key" : "红色",
"doc_count" : 4,
"avg_price" : {
"value" : 3250.0
}
},
有了平均值,还要有最大,最小,求和
#query
{
"size": 0,
"aggs": {
"colors": {
"terms": {
"field": "color"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
},
"min_price": {
"min": {
"field": "price"
}
},
"max_price": {
"max": {
"field": "price"
}
},
"sum_price": {
"sum": {
"field": "price"
}
}
}
}
}
}
#result
{
"key" : "红色",
"doc_count" : 4,
"max_price" : {
"value" : 8000.0
},
"min_price" : {
"value" : 1000.0
},
"avg_price" : {
"value" : 3250.0
},
"sum_price" : {
"value" : 13000.0
}
}
在颜色区分的时候,再把相关品牌展示出来,这个就涉及到桶的嵌套
#query
{
"size": 0,
"aggs": {
"colors": {
"terms": {
"field": "color"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
},
"brand": {
"terms": {
"field": "brand"
}
}
}
}
}
}
#result
{
"key" : "红色",
"doc_count" : 4,
"avg_price" : {
"value" : 3250.0
},
"brand" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "长虹",
"doc_count" : 3
},
{
"key" : "三星",
"doc_count" : 1
}
]
}
}
往往现实中需求很写实,所以还有铵价格区间统计销售量和销售额(直方图)
histogram 也是bucket ,他按照某个值指定的interval(步长),划分一个一个的bucket
interval:2000,0~1999,2000~3999,4000~5999,6000~7999,8000~9999,buckets
去根据price的值2500,此时就会将这条数据放入2000~4000对应的那个bucket中
我们上面这条数据{"price":1999,"color":"红色","brand":"长虹","sold_date":"2021-10-28"}
用来测试是2000 还是1999
GET /tvs/_search
#query
{
"size": 0,
"aggs": {
"price": {
"histogram": { #类型为n. [统计] 直方图;柱状图
"field": "price",
"interval": 2000
},
"aggs": {
"revenue": {
"sum": {
"field": "price"
}
}
}
}
}
}
#result
"aggregations" : {
"price" : {
"buckets" : [
{
"key" : 0.0,
"doc_count" : 4,
"revenue" : {
"value" : 5699.0
}
},
{
"key" : 2000.0,
"doc_count" : 4,
"revenue" : {
"value" : 9500.0 # 这里的值代表2000-3999价格区间的累加
}
},
{
"key" : 4000.0,
"doc_count" : 0,
"revenue" : {
"value" : 0.0
}
},
{
"key" : 6000.0,
"doc_count" : 0,
"revenue" : {
"value" : 0.0
}
},
{
"key" : 8000.0,
"doc_count" : 1,
"revenue" : {
"value" : 8000.0
}
}
]
}
}
销售中很常见的一种场景,按照年度、季度、月份、周来统计销售量,那就要用到calender_interval
calender_interval =1m 那间隔就是一个月 d一天 w一周 y 一年 h 小时 季度 quarter
这里间隔是每月一个区间
`#query
{
"size": 0,
"aggs": {
"sales": {
"date_histogram": {
"field": "sold_date",
"calendar_interval":"1w",
"format": "yyyy-mm-dd"
}
}
}
}
#result
"aggregations" : {
"sales" : {
"buckets" : [
{
"key_as_string" : "2016-00-16",
"key" : 1463356800000,
"doc_count" : 1
},
{
"key_as_string" : "2016-00-23",
"key" : 1463961600000,
"doc_count" : 0
},
`
上述结果会把doc_count 为零的也显示出来,这样就满足了一些顽固产品的需求 即这周没有销售额
也要给我显示出来。如果不想显示出来 则在请求的时候增加 min_doc_count
{
"query":{
"size":0,
"aggs":{
"sales":{
"date_histogram":{
"field":"sold_date",
"calender_interval":"quarter",
"format": "yyyy-mm-dd",
"min_doc_count":1 # 最少有1个才返回
}
}
}
}
}
#result
{
"key_as_string" : "2016-04-01",
"key" : 1459468800000,
"doc_count" : 1
},`
{
"key_as_string" : "2016-07-01",
"key" : 1467331200000,
"doc_count" : 2
},
{
"key_as_string" : "2016-10-01",
"key" : 1475280000000,
"doc_count" : 3
},`
假如有个产品告诉你,及时在某个时间范围内没有搜索到,你也要展示出来,那你就要用
"extended_bounds": { "min": "2016-01-01", "max": "2017-12-31" }
写在min_doc_count 同级的位置,这样即使数据集里没有2016年第一季度的数据,仍然会
有一条doc_count为0的数据
{
"key_as_string" : "2016-01-01",
"key" : 1451606400000,
"doc_count" : 0 # 就这条
},
{
"key_as_string" : "2016-04-01",
"key" : 1459468800000,
"doc_count" : 1
}
但是如果同时设置min_doc_count 大于 0 和extended_bounds 优先级较高的是前者.
我们构建聚合以便按季度展示所有家电品牌总销售额。同时按季度、按每个家电品牌计算销售总额,以便可以找出哪种品牌最赚钱(出了点岔子,密码不对了重置了下密码)
#query
{
"size": 0,
"aggs": {
"sales": {
"date_histogram": {
"field": "sold_date",
"calendar_interval": "quarter",
"format": "yyyy-MM-dd",
"min_doc_count": 1,
"extended_bounds": {
"min": "now/d",
"max": "now/d"
}
},
"aggs": {
"per_make_sum": {
"terms": {
"field": "brand",
"size": 10
},
"aggs": {
"sum_price": {
"sum": {
"field": "price" #计算每种品牌的总销售金额
}
}
}
},
"total_sum": {
"sum": {
"field": "price" #也计算所有全部品牌的汇总销售金额。
}
}
}
}
}
}
#result
{
"key_as_string" : "2017-01-01",
"key" : 1483228800000,
"doc_count" : 2,
"per_make_sum" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "三星",
"doc_count" : 1,
"sum_price" : {
"value" : 8000.0
}
},
{
"key" : "小米",
"doc_count" : 1,
"sum_price" : {
"value" : 2500.0
}
}
]
},
"total_sum" : {
"value" : 10500.0
}
},
快结束了,再坚持一下哦。桶的嵌套,让家电按照颜色来分组然后计算其平均价格,再按品牌分组,再计算其平均价格
#query
{
"size": 0,
"aggs": {
"group_by_color": {
"terms": {
"field": "color"
},
"aggs": {
"color_avg_price": {
"avg": {
"field": "price"
}
},
"group_by_brand": {
"terms": {
"field": "brand"
},
"aggs": {
"brand_avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
}
}
#result
{
"key" : "绿色",
"doc_count" : 2,
"color_avg_price" : {
"value" : 2100.0
},
"group_by_brand" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "TCL",
"doc_count" : 1,
"brand_avg_price" : {
"value" : 1200.0
}
},
{
"key" : "小米",
"doc_count" : 1,
"brand_avg_price" : {
"value" : 3000.0
}
}
]
}
}
参考链接:https://blog.csdn.net/ma_jiang/article/details/113763584