bucket与metric两个核心概念
- 初始化数据
city name
北京 小李
北京 小王
上海 小张
上海 小丽
上海 小陈
-
按照某个字段进行bucket划分,那个字段的值相同的那些数据,就会被划分到一个bucket中、比如基于city划分buckets,划分出来两个bucket,一个是北京bucket,一个是上海bucket(类似于sql的group by city)
1) 北京bucket:包含了2个人,小李,小王
2)上海bucket:包含了3个人,小张,小丽,小陈 -
类比sql:select max(score) from student group by sex的分组聚合,首先第一步就是分组,第二步是对每个组内的数据进行聚合分析。分组对应的就是bucket;聚合对应的就是metric
metric:对一个数据分组执行的统计
当我们有了一堆bucket之后,就可以对每个bucket中的数据进行聚合分词了,比如说计算一个bucket内所有数据的数量,或者计算一个bucket内所有数据的平均值,最大值,最小值
准备练习数据
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" }
统计哪种颜色电视销量最高
# 等价于select color,count(*) from tbl group by color
GET /tvs/_search
{
"size": 0,
"aggs": {
"什么颜色的电视销量最高": {
"terms": {
"field": "color"
}
}
}
}
- size:只获取聚合结果,而不要执行聚合的原始数据
- aggs:固定语法,要对一份数据执行分组聚合操作
- 什么颜色的电视销量最高:就是对每个aggs操作都要起一个名字,这个名字是随意的,取什么都ok
- terms:根据字段的值进行分组
- field:根据指定的字段的值进行分组
疑问:可不可以一次性对多个字段进行group by
统计每种颜色电视平均价格
GET /tvs/_search
{
"size": 0,
"aggs": {
"每种的颜色电视": {
"terms": {
"field": "color"
},
"aggs": {
"平均价格": {
"avg": {
"field": "price"
}
}
}
}
}
}
- 按照color去分bucket,默认分完bucket后还会统计每个bucket中的文档个数,因为这是bucket操作默认执行的一个内置metric——doc_count。
- 如果要自定义metric聚合操作,在一个aggs执行的bucket操作(terms)平级的json结构下再加一个aggs,这个第二个aggs内部,同样取个名字,执行一个metric操作,avg,对之前的每个bucket中的数据的指定的field,price field,求一个平均值
疑问:可不可以对不同的bucket执行不同的metric?
bucket嵌套实现颜色+品牌的多层下钻,分析平均价格
什么是下钻?
下钻:
基于当前的数据,在继续进行分组聚合操作
。比如说颜色的分组,然后还要继续对这个分组内的数据再分组,比如一个颜色内,还可以分成多个不同的品牌的组,最后对每个最小粒度的分组执行聚合分析操作,这就叫做下钻分析。
实现从颜色到品牌进行下钻分析:每种颜色的平均价格,以及找到每种颜色每个品牌的平均价格。
比如说,现在红色的电视有4台,同时这4台电视中,有3台是属于长虹的,1台是属于小米的
红色电视中的3台长虹的平均价格是多少?
红色电视中的1台小米的平均价格是多少?
es下钻分析,就要对bucket进行多层嵌套,多次分组
按照多个维度(颜色+品牌)多层下钻分析,而且学会了每个下钻维度(颜色,颜色+品牌),都可以对每个维度分别执行一次metric聚合操作
GET /tvs/_search
{
"size": 0,
"aggs": {
"颜色": {
"terms": {
"field": "color"
},
"aggs": {
"平均价格": {
"avg": {
"field": "price"
}
},
"品牌": {
"terms": {
"field": "brand"
},
"aggs": {
"平均价格": {
"avg": {
"field": "price"
}
}
}
}
}
},
"最高价格": {
"max": {
"field": "price"
}
}
}
}
总结:aggs里面只有分组条件和聚合条件,并且
一个aggs里最多只能有一个分组条件;但是可以有任意个聚合条件
。我们可以用aggs+分组条件嵌套aggs+分组条件,不断嵌套下去往下分组以实现下钻的功能。
统计每种颜色电视最大最小,总和价格
GET /tvs/_search
{
"size": 0,
"aggs": {
"颜色": {
"terms": {
"field": "color"
},
"aggs": {
"最高价格": {
"max": {
"field": "price"
}
},
"最低价格": {
"min": {
"field": "price"
}
},
"总和价格": {
"sum": {
"field": "price"
}
}
}
}
}
histogram按价格区间统计电视销量和销售额
GET /tvs/_search
{
"size": 0,
"aggs": {
"价格区间": {
"histogram": {
"field": "price",
"interval": 2000
},
"aggs": {
"销售额": {
"sum": {
"field": "price"
}
}
}
}
}
}
date hitogram之统计每月电视销量
GET /tvs/_search
{
"size": 0,
"aggs": {
"电视销量": {
"date_histogram": {
"field": "sold_date",
"interval": "month",
"extended_bounds": {
"min": "2016-01-01"
},
"min_doc_count": 1,
"format": "yyyy-MM-dd"
},
"aggs": {
"销售额": {
"sum": {
"field": "price"
}
}
}
}
}
}
- date histogram,按照指定的某个date类型的日期field,以及日期interval,按照一定的日期间隔,去划分bucket
-
date interval = 1m,
2017-01-01~2017-01-31,就是一个bucket
2017-02-01~2017-02-28,就是一个bucket
然后会去扫描每个数据的date field,判断date落在哪个bucket中,就将其放入那个bucket。2017-01-05,就将其放入2017-01-01~2017-01-31,就是一个bucket - min_doc_count:默认为0,表示当桶中文档的数量最少也要等于min_doc_count才会显示出来,否则会过滤掉。比如,2017-01-01~2017-01-31中,一条数据都没有,如果想过滤它则写min_doc_count:1
- extended_bounds,min,max:划分bucket的时候,会限定在这个起始日期,和截止日期内
下钻分析之统计每季度每个品牌的销售额
GET /tvs/_search
{
"size": 0,
"aggs": {
"季度": {
"date_histogram": {
"field": "sold_date",
"interval": "quarter",
"min_doc_count": 0,
"format": "yyyy-MM"
},
"aggs": {
"品牌": {
"terms": {
"field": "brand"
},
"aggs": {
"销售额": {
"sum": {
"field": "price"
}
}
}
}
}
}
}
}
统计小米有什么颜色的手机
GET /tvs/_search
{
"size": 0,
"query": {
"term": {
"brand": {
"value": "小米"
}
}
},
"aggs": {
"颜色": {
"terms": {
"field": "color"
}
}
}
}
aggs是基于query过滤后的范围进行分组的
小米的平均价格 对比所有手机的平均价格
GET /tvs/_search
{
"size": 0,
"query": {
"term": {
"brand": {
"value": "小米"
}
}
},
"aggs": {
"小米手机": {
"avg": {
"field": "price"
}
},
"所有手机": {
"global": {},
"aggs": {
"平均价格": {
"avg": {
"field": "price"
}
}
}
}
}
}
- global,让聚合的范围忽略query,就是将所有数据纳入聚合的scope,而不管之前的query。
统计价格大于1200的电视平均价格
GET /tvs/_search
{
"size": 0,
"query": {
"constant_score": {
"filter": {
"range": {
"price": {
"gte": 1200
}
}
}
}
},
"aggs": {
"平均价格": {
"avg": {
"field": "price"
}
}
}
}
- constant_score:表示该条件的得分是静态的,不用动态计算得分。
- constant_score.filter:filter之所以被constant_score包裹,是因为被过滤的数据是不关心得分的。
bucket filter:统计牌品2016年的平均价格
TODO
排序:按每种颜色的平均销售额降序排序
TODO
去重cardinality:根据销售数据获取共有多少种品牌
# 类似于 select count(*) from tvs group by brand
GET /tvs/_search
{
"size": 0,
"aggs": {
"所有的品牌": {
"cardinality": {
"field": "brand"
}
}
}
}
# 校验
GET /tvs/_search
{
"aggs": {
"所有的品牌": {
"terms": {
"field": "brand"
}
}
}
}