elasticsearch聚合查询
1.bucket和metric的概念
bucket是聚合查询的数据分组, 小学学生用年级分组,六个年级得到了6个bucket,bucket就是每个年级下的学生
metric就是分组之后,统计分析. 求和,最大值,最小值,平均值等
类似sql的语法的group having
数据准备
创建数据映射关系
PUT /cars
{
"mappings": {
"properties": {
"price": {
"type": "long"
},
"color": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"model": {
"type": "keyword"
},
"sold_date": {
"type": "date"
},
"remark": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
创建数据
POST /cars/_bulk
{ "index": {}}
{ "price" : 258000, "color" : "金色", "brand":"大众", "model" : "大众迈腾", "sold_date" : "2021-10-28","remark" : "大众中档车" }
{ "index": {}}
{ "price" : 123000, "color" : "金色", "brand":"大众", "model" : "大众速腾", "sold_date" : "2021-11-05","remark" : "大众神车" }
{ "index": {}}
{ "price" : 239800, "color" : "白色", "brand":"标志", "model" : "标志508", "sold_date" : "2021-05-18","remark" : "标志品牌全球上市车型" }
{ "index": {}}
{ "price" : 148800, "color" : "白色", "brand":"标志", "model" : "标志408", "sold_date" : "2021-07-02","remark" : "比较大的紧凑型车" }
{ "index": {}}
{ "price" : 1998000, "color" : "黑色", "brand":"大众", "model" : "大众辉腾", "sold_date" : "2021-08-19","remark" : "大众最让人肝疼的车" }
{ "index": {}}
{ "price" : 218000, "color" : "红色", "brand":"奥迪", "model" : "奥迪A4", "sold_date" : "2021-11-05","remark" : "小资车型" }
{ "index": {}}
{ "price" : 489000, "color" : "黑色", "brand":"奥迪", "model" : "奥迪A6", "sold_date" : "2022-01-01","remark" : "政府专用?" }
{ "index": {}}
{ "price" : 1899000, "color" : "黑色", "brand":"奥迪", "model" : "奥迪A 8", "sold_date" : "2022-02-12","remark" : "很贵的大A6。。。" }
聚合查询
1、根据color分组统计销售数量
只执行聚合分组,不做复杂的聚合统计。在ES中最基础的聚合为terms,相当于SQL中的count。
在ES中默认为分组数据做排序,使用的是doc_count数据执行降序排列。可以使用_key元数据,根据分组后的字段数据执行不同的排序方案,也可以根据_count元数据,根据分组后的统计值执行不同的排序方案。
GET /cars/_search
{
"size":0, //不返回数值
"aggs": { //聚合
"group_by_color": { //名称 可自定义
"terms": {
"field": "color", //通过color聚合
"order": {
"_count": "desc" //排序 分组之后 组内元素个数排序
}
}
}
}
2、统计不同color车辆的平均价格
本案例先根据color执行聚合分组,在此分组的基础上,对组内数据执行聚合统计,这个组内数据的聚合统计就是metric。同样可以执行排序,因为组内有聚合统计,且对统计数据给予了命名avg_by_price,所以可以根据这个聚合统计数据字段名执行排序逻辑。
GET /cars/_search
{
"size":0,
"aggs": { //外层分组
"group_by_color": {
"terms": {
"field": "color",
"order": {
"avg_by_price": "asc"
}
},
"aggs": { //外层分组之后,组内数据求平均值
"avg_by_price": { //自定义
"avg": { //平均值
"field": "price" //通过price字段求平均值
}
}
}
}
}
}
通过颜色分组,得到组数据,每一组再次通过品牌分组,每一个品牌的平均值
嵌套式的分组成为下钻分析
GET /cars/_search
{
"size": 0,
"aggs": {
"group_by_color": {
"terms": {
"field": "color"
},
"aggs": {
"group_by_brand": {
"terms": {
"field": "brand",
"order": {
"avg_by_price": "desc" //通过得到的平均价格排序 使用下面自定义名称
}
},
"aggs": {
"avg_by_price": { //名称自定义
"avg": {
"field": "price"
}
}
}
}
}
}
}
}
aggs是同级为平铺分析
GET /cars/_search
{
"aggs": {
"group_by_color": { //外层分组通过颜色
"terms": {
"field": "color",
"order": {
"avg_by_price_color": "asc"
}
},
"aggs": { //每个颜色分组之后,汽车分平均价格
"avg_by_price_color": {
"avg": {
"field": "price"
}
},
"group_by_brand": { //每个颜色分组之后 品牌分组
"terms": {
"field": "brand",
"order": {
"avg_by_price_brand": "desc"
}
},
"aggs": {
"avg_by_price_brand": { // 颜色分组/品牌分组 平均价格
"avg": {
"field": "price"
}
}
}
}
}
}
}
}
统计不同color中的最大和最小价格、总价
GET /cars/_search
{
"aggs": {
"group_by_color": { //颜色分组
"terms": {
"field": "color"
},
"aggs": {
"max_price": { //颜色分组之后 最高价格
"max": {
"field": "price"
}
},
"min_price": { //最小价格
"min": {
"field": "price"
}
},
"sum_price": { //总价格
"sum": {
"field": "price"
}
}
}
}
}
}
统计不同品牌汽车中价格排名最高的车型
在分组后,可能需要对组内的数据进行排序,并选择其中排名高的数据。那么可以使用s来实现:top_top_hithits中的属性size代表取组内多少条数据(默认为10);sort代表组内使用什么字段什么规则排序(默认使用_doc的asc规则排序);_source代表结果中包含document中的那些字段(默认包含全部字段)。
GET cars/_search
{
"size": 0,
"aggs": {
"group_by_brand": {
"terms": {
"field": "brand" //品牌分组
},
"aggs": {
"top_car": { //名称自定义
"top_hits": { //通过price排序之后,取第一个,也就是价格最高
"size": 1,
"sort": [
{
"price": {
"order": "desc"
}
}
],
"_source": { //只返回特定的字段
"includes": [
"model",
"price"
]
}
}
}
}
}
}
}
区间统计
histogram 区间统计
histogram类似terms,也是进行bucket分组操作的,是根据一个field,实现数据区间分组。
如:以100万为一个范围,统计不同范围内车辆的销售量和平均价格。那么使用histogram的聚合的时候,field指定价格字段price。区间范围是100万-interval : 1000000。这个时候ES会将price价格区间划分为: [0, 1000000), [1000000, 2000000), [2000000, 3000000)等,依次类推。在划分区间的同时,histogram会类似terms进行数据数量的统计(count),可以通过嵌套aggs对聚合分组后的组内数据做再次聚合分析。
GET /cars/_search
{
"size":0,
"aggs": {
"histogram_by_price": {
"histogram": { //区间
"field": "price",
"interval": 100000 //10万分一组
},
"aggs": {
"avg_by_price": {
"max": { //每个区间内最大的价格
"field": "price"
}
}
}
}
}
}
date_histogram可以对date类型的field执行区间聚合分组,如每月销量,每年销量等。
如:以月为单位,统计不同月份汽车的销售数量及销售总金额。这个时候可以使用date_histogram实现聚合分组,其中field来指定用于聚合分组的字段,interval指定区间范围(可选值有:year、quarter、month、week、day、hour、minute、second),format指定日期格式化,min_doc_count指定每个区间的最少document(如果不指定,默认为0,当区间范围内没有document时,也会显示bucket分组),extended_bounds指定起始时间和结束时间(如果不指定,默认使用字段中日期最小值所在范围和最大值所在范围为起始和结束时间)。
在kibana dashboard看板的时候,时序图经常使用时间区域作为x轴查看数据
7.x之前
GET /cars/_search
{
"aggs": {
"histogram_by_date": { //名称自定义
"date_histogram": { //时间区间
"field": "sold_date", //划分字段
"interval": "month", //时间区间单位月
"format": "yyyy-MM-dd", //时间格式化
"min_doc_count": 1, //每个区间最少的数量,如果2,区域只有1个数据,则该月不展示
"extended_bounds": { //指定总体时间区域
"min": "2021-01-01",
"max": "2022-12-31"
}
},
"aggs": {
"sum_by_price": {
"sum": {
"field": "price"
}
}
}
}
}
}
7.x之后
GET /cars/_search
{
"aggs": {
"histogram_by_date": {
"date_histogram": {
"field": "sold_date",
"calendar_interval": "month", //7.x之后变化的字段
"format": "yyyy-MM-dd",
"min_doc_count": 1,
"extended_bounds": {
"min": "2021-01-01",
"max": "2022-12-31"
}
},
"aggs": {
"sum_by_price": {
"sum": {
"field": "price"
}
}
}
}
}
}
在聚合统计数据的时候,有些时候需要对比部分数据和总体数据。
如:统计某品牌车辆平均价格和所有车辆平均价格。global是用于定义一个全局bucket,这个bucket会忽略query的条件,检索所有document进行对应的聚合统计
GET /cars/_search
{
"size": 0,
"query": { //首先获取大众数据
"match": {
"brand": "大众"
}
},
"aggs": { //通过获取的大众数据,继续分析
"volkswagen_of_avg_price": {
"avg": { //大众的平均价格
"field": "price"
}
},
"all_avg_price": {
"global": {}, //把大众条件去掉,后去全部数据
"aggs": { //全部数据求平均值
"all_of_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
如果有多层aggs,执行下钻聚合的时候,也可以根据最内层聚合数据执行排序。
如:统计每个品牌中每种颜色车辆的销售总额,并根据销售总额降序排列。这就像SQL中的分组排序一样,只能组内数据排序,而不能跨组实现排序。
GET /cars/_search
{
"aggs": {
"group_by_brand": {
"terms": { //品牌分组,默认排序是品牌数量多在前
"field": "brand"
},
"aggs": {
"group_by_color": {
"terms": { //品牌分组完之后,在用颜色分组
"field": "color",
"order": { // 颜色分组内部,用总价排序
"sum_of_price": "desc"
}
},
"aggs": {
"sum_of_price": {
"sum": {
"field": "price"
}
}
}
}
}
}
}
}
filter也可以使用在aggs句法中,filter的范围决定了其过滤的范围。
如:统计某品牌汽车最近一年的销售总额。将filter放在aggs内部,代表这个过滤器只对query搜索得到的结果执行filter过滤。如果filter放在aggs外部,过滤器则会过滤所有的数据。
12M/M 表示 12 个月。
1y/y 表示 1年。
d 表示天
GET /cars/_search
{
"query": { //获取大众数据
"match": {
"brand": "大众"
}
},
"aggs": { //开始聚合
"count_last_year": {
"filter": { //聚合之前,对大众的数据再次过滤 12m之内,也就是一年
"range": {
"sold_date": {
"gte": "now-12M"
}
}
},
"aggs": { //销售总额
"sum_of_price_last_year": {
"sum": {
"field": "price"
}
}
}
}
}
}