当当当~,聚合数据的时候先缩小一下数据范围,然后再排序聚合,这样理论上来讲是会快很多。
我们还是上次的数据,数据准备
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"}
1 先缩小数据范围再聚合分析,通过品牌搜索,统计销售额
#query
GET /tvs/_search
{
"size": 0,
"query": {
"term": {
"brand": {
"value": "小米" #品牌为小米
}
}
},
"aggs": {
"group_by_color": {
"terms": {
"field": "color" #以颜色分组
}
}
}
}
#result
"buckets" : [
{
"key" : "绿色",
"doc_count" : 1
},
{
"key" : "蓝色",
"doc_count" : 1
}
]
2 单品牌与所有品牌的销量对比,场景:一个品牌在所有销售中的占比
#query
{
"size": 0,
"query": {
"term": {
"brand": {
"value": "小米"
}
}
},
"aggs": {
"single_brand_avg_price": {
"avg": {
"field": "price"
}
},
"all": {
"global": {},
"aggs": {
"all_brand_avg_price": {
"avg": {
"field": "price"
}
},
"all_brand_total_price": {
"sum": {
"field": "price"
}
}
}
}
}
}
先对小米品牌做个搜索,其中singe_brand_avg_price是针对query中小米销售平均价格
all.all_brand_avg_price 拿到所有品牌的平均价格
all.all_brand_total_price 拿到所有品牌的总价格
#result
"aggregations" : {
"all" : {
"doc_count" : 9,
"all_brand_total_price" : {
"value" : 23199.0
},
"all_brand_avg_price" : {
"value" : 2577.6666666666665 #注意精度问题
}
},
"single_brand_avg_price" : {
"value" : 2750.0
}
}
3 按照价格大于某要求,然后计算平均价格,这个是filter
#query
{
"size": 0,
"query": {
"constant_score": {
"filter": {
"range": {
"price": {
"gt": 1500
}
}
}
}
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
#result
"aggregations" : {
"avg_price" : {
"value" : 3249.8333333333335
}
}
4 按时间段、品牌统计销售额(过滤桶)
#query
{
"size": 0,
"query": {
"term": {
"brand": {
"value": "长虹"
}
}
},
"aggs": {
"recent_150d": {
"filter": {
"range": {
"sold_date": {
"gte": "now-1M"
}
}
},
"aggs":{
"recent_150d_avg_price":{
"avg": {
"field": "price"
}
}
}
}
}
}
#result
"aggregations" : {
"recent_150d" : {
"doc_count" : 1,
"recent_150d_avg_price" : {
"value" : 1999.0
}
}
}
5 排序(哈哈哈哈哈,终于到排序了),按照颜色分组,价格排序
#query
{
"size": 0,
"aggs": {
"group_by_color": {
"terms": {
"field": "color",
"order": {
"avg_price": "asc" #asc是升序 desc降序,key是下面aggs.avg_price
}
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
#result
"aggregations" : {
"group_by_color" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "蓝色",
"doc_count" : 2,
"avg_price" : {
"value" : 2000.0
}
},
{
"key" : "绿色",
"doc_count" : 2,
"avg_price" : {
"value" : 2100.0
}
},
{
"key" : "红色",
"doc_count" : 5,
"avg_price" : {
"value" : 2999.8
}
}
]
}
}
先按颜色分组,再按品牌分组,再按价格倒序
#query
{
"size": 0,
"aggs": {
"group_by_color": {
"terms": {
"field": "color"
},
"aggs": {
"group_by_brand": {
"terms": {
"field": "brand",
"order": {
"avg_price": "desc"
}
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
}
}
#result
{
"key" : "红色",
"doc_count" : 5,
"group_by_brand" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "三星",
"doc_count" : 1,
"avg_price" : {
"value" : 8000.0
}
},
{
"key" : "长虹",
"doc_count" : 4,
"avg_price" : {
"value" : 1749.75
}
}
]
}
}
6 后过滤器
目前为止,我们可以同时对搜索结果和聚合结果进行过滤(不计算得分的 filter 查询),以及针对聚合结果的一部分进行过滤( filter 桶)。我们可能会想,"只过滤搜索结果,不过滤聚合结果呢?" 答案是使用 post_filter 。
它是接收一个过滤器的顶层搜索请求元素。这个过滤器在查询 之后 执行(这正是该过滤器的名字的由来:它在查询之后 post 执行)。正因为它在查询之后执行,它对查询范围没有任何影响,所以对聚合也不会有任何影响
#query
GET /tvs/_search
{
"size": 0,
"query": {
"match": {
"brand": "TCL"
}
},
"post_filter": {
"term": {
"color": "蓝色"
}
},
"aggs": {
"all_colors": {
"terms": {
"field": "color"
}
}
}
}
#result
"hits" : {
"total" : {
"value" : 1,#注意这里
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"all_colors" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "绿色",
"doc_count" : 1
},
{
"key" : "蓝色",
"doc_count" : 1
}
]
}
}
}
查询 部分找到所有的 TCL 家电,然后用 terms 聚合创建一个颜色列表。因为聚合对查询范围进行操作,颜色列表与TCL有的颜色相对应。
最后, post_filter 会过滤搜索结果,只展示蓝色 TCL 家电。这在查询执行过 后 发生,所以聚合不受影响。
这通常对 UI 的连贯一致性很重要,可以想象用户在界面商选择了一类颜色(比如:绿色),期望的是搜索结果已经被过滤了,而 不是 过滤界面上的选项。如果我们应用 filter 查询,界面会马上变成 只 显示 绿色 作为选项,这不是用户想要的。靠我第一遍没看懂,md为什么限定了蓝色还会出现绿色。答案在于hits里面的total.value的值 当使用过post_filter(后置过滤器)的时候 执行顺序是 先query、再aggs 最后才是post_filter。这里蓝色的TCl家电只有一台,所以total.value 的值是1
6 统计去重的数量
网站独立访客是多少?
卖了多少种汽车?
每月有多少独立用户购买了商品?
例如:实现 SELECT COUNT(DISTINCT color) FROM product;
这种要用到cardinality(基数)
#query
{
"size" : 0,
"aggs" : {
"distinct_colors" : {
"cardinality" : {
"field" : "color",
"precision_threshold": 100 #精度控制 0--40000 溢出仍为40000
}
}
}
}
#result
"aggregations" : {
"distinct_colors" : {
"value" : 3 # 有三种不同的产品在出售
}
}
cardinality 度量是一个近似算法。 它是基于 HyperLogLog++ (HLL)算法的。 HLL 会先对我们的输入作哈希运算,然后根据哈希运算的结果中的 bits 做概率估算从而得到基数。
算法的特性 :
可配置的精度,用来控制内存的使用(更精确 = 更多内存)。小的数据集精度是非常高的。
我们可以通过配置参数,来设置去重需要的固定内存使用量。无论数千还是数十亿的唯一值,内存使用量只与你配置的精确度相关。