ES聚合指标

本文介绍了如何在Elasticsearch中使用聚合功能进行搜索,如计算酒店价格的平均值、最大值、最小值以及统计非空值数量。文章详细讲解了聚合指标的选择、常用的聚合类型(如avg、max、min、sum、stats和value_count)以及如何处理空值,同时给出了Java代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

聚合

当用户使用搜索引擎完成搜索后,在展示结果钟需要进行进一步的筛选,而筛选的维度需要根据当前的搜索结果进行汇总,这就用到了聚合技术。聚合的需求在很多应用程序钟都有所体现,例如在京东搜索“咸鸭蛋”,然后点击搜索界面钟的筛选按钮,在弹出的界面钟可以对当前的搜索结果进行进一步的过滤。例如,可以重价格区间、品牌、分类i、个数等维度分别进行筛选。

4f856c6d62bcc25d863cfd3e3d068a6

为方面介绍后续内容,这里定义酒店的索引如下:


json

复制代码

 PUT /hotel_poly  {    "settings": {      "number_of_shards": 1 //指定主分片个数为1   },    "mappings": {      "properties": {        "title":{          "type": "text"       },        "city":{          "type": "keyword"       },        "price":{          "type": "double"       },        "create_time":{          "type": "date"       },        "full_room":{          "type": "boolean"       },        "location":{          "type": "geo_point"       },        "tags":{          "type": "keyword"       },        "comment_info":{          "properties": {            "favourable_comment":{              "type":"integer"           },            "negative_comment":{              "type":"integer"           }         }       }     }   }  }

向索引中写入示例数据


bash

复制代码

 POST /_bulk  {"index":{"_index":"hotel_poly","_id":"001"}}  {"title":"文雅假日酒店","city":"北京","price":556.00,"create_time":"20200418120000","full_room":true,"location":{"lat":39.938838,"lon":106.449112},"tags":["wifi","小型电影院"],"comment_info":{"favourable_comment":20,"negative_comment":10}}  {"index":{"_index":"hotel_poly","_id":"002"}}  {"title":"金都嘉怡假日酒店","city":"北京","create_time":"20210315200000","full_room":false,"location":{"lat":39.915153,"lon":116.4030},"tags":["wifi","免费早餐"],"comment_info":{"favourable_comment":20,"negative_comment":10}}  {"index":{"_index":"hotel_poly","_id":"003"}}  {"title":"金都假日酒店","city":"北京","price":200.00,"create_time":"20210509160000","full_room":true,"location":{"lat":40.002096,"lon":116.386673},"comment_info":{"favourable_comment":20,"negative_comment":10}}  {"index":{"_index":"hotel_poly","_id":"004"}}  {"title":"金都假日酒店","city":"天津","price":500.00,"create_time":"20210218080000","full_room":false,"location":{"lat":39.155004,"lon":117.203976},"tags":["wifi","免费车位"]}  {"index":{"_index":"hotel_poly","_id":"005"}}  {"title":"文雅精选酒店","city":"天津","price":800.00,"create_time":"20210101080000","full_room":true,"location":{"lat":39.178447,"lon":117.219999},"tags":["wifi","充电车位"],"comment_info":{"favourable_comment":20,"negative_comment":10}}

1.1 聚合指标

在进行聚合搜索时,聚合的指标业务需求不仅是文档数量。例如,在酒店搜索场景中,我们希望看到以当前位置为中心点,周边各个区域酒店的平均加个。

1.1.1 常见的聚合指标

在搜索聚合时,用户可能关注字段的相关统计信息,例如平均值、最大值、最小值及加和值。例如,用户在使用一个二手房交易搜索引擎进行搜索时,可能会关注当前城市各个区域的房产平均价格。用户在搜索酒店时,也可能会关注附近各个区域酒店的最低价格。

ES聚合请求的地址也是索引的搜索地址,可以使用aggs子句封装聚合请求。

当使用avg子句进行平均值的聚合时,可以在avg子句中指定聚合的字段。在默认情况下,查询将匹配所有文档,如果不需要返回匹配的文档信息,最好将返回的文档个数设置为0。这样既可以让结果看起来更整洁,又可以提高查询速度。

下面的DSL将查询所有酒店的平均价格并且不反悔匹配的文档信息。


json

复制代码

 GET /hotel_poly/_search  {    "size": 0,    "aggs": {      "my_agg": { //聚合名称        "avg": {          "field": "price" //计算文档的平均价格       }     }   }  }

ES返回结果如下:

image-20240421103943022

在上面的搜索结果中,索引的5个文档全部命中,由于DSL设置size为0,所以命中文档的信息没有显示。在搜索结果的aggregations子句中存储着聚合结果,其中my_agg是聚合的名称,其对应的value值就是具体聚合结果,即酒店的平均价格。

如果聚合的指标字段不是ES的基本类型,例如object类型,则可以使用点运算符进行引用。下面的DSL演示了该用法:


bash

复制代码

 GET /hotel_poly/_search  {    "size": 0,    "aggs":{      "my_agg":{        "avg": {          "field": "comment_info.favourable_comment"       }     }   }  }

image-20240421104222141

与平均值类似,最大值、最小值及加和值分别使用maxminsum子句进行聚合,不再赘述。

以下代码演示了在Java中使用聚合计算平均值的逻辑。


ini

复制代码

 @Test  public void getAvgAggSearch() throws IOException {      //创建搜索请求      SearchRequest searchRequest = new SearchRequest("hotel_poly");      SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();      String aggName="my_agg";   //聚合的名称      //定义avg聚合,指定字段为price      AvgAggregationBuilder aggregationBuilder = AggregationBuilders.avg(aggName).field("price");      searchSourceBuilder.aggregation(aggregationBuilder);    //添加聚合      searchRequest.source(searchSourceBuilder);  //设置查询请求      //执行查询      SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);      //获取聚合结果      Aggregations aggregations = searchResponse.getAggregations();      Avg avg = aggregations.get(aggName);    //获取avg聚合返回的对象      String key = avg.getName();                     //获取聚合名称      double avgValue = avg.getValue();      System.out.println("key="+key+",aggValue="+avgValue);  }

image-20240421104806086

为了避免多次请求,ES还提供了stats聚合。stats聚合可以将对应字段的最大值、最小值、平均值及加和值一起计算并返回计算结果。下面的DSL展示了stats的用法。


bash

复制代码

 GET /hotel_poly/_search  {    "size": 0,    "aggs":{      "my_agg":{        "stats": {          "field": "price"       }     }   }  }

image-20240421105354075

以下代码演示了在Java中使用stats聚合的逻辑。


ini

复制代码

 @Test  public void getStatsAggSearch() throws IOException{      //创建搜索请求      SearchRequest searchRequest = new SearchRequest("hotel_poly");      SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();      String aggName="my_agg";   //聚合的名称      //定义avg聚合,指定字段为price      StatsAggregationBuilder aggregationBuilder = AggregationBuilders.stats(aggName).field("price");      searchSourceBuilder.aggregation(aggregationBuilder);    //添加聚合      searchRequest.source(searchSourceBuilder);  //设置查询请求      //执行查询      SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);      //获取聚合结果      Aggregations aggregations = searchResponse.getAggregations();      Stats stats = aggregations.get(aggName);    //获取stats聚合返回的对象      String key = stats.getName();      double sumValue=stats.getSum();             //获取聚合加和值      double avgVal=stats.getAvg();               //获取聚合平均值      double countVal=stats.getCount();           //获取聚合文档数量值      double maxVal=stats.getMax();               //获取聚合最大值      double minVal=stats.getMin();               //获取聚合最小值      log.info("key={}",key);                     //打印聚合名称      log.info("sumVal={},avgVal={},countVal={},maxVal={},minVal={}",sumValue,avgVal,countVal,maxVal,minVal);  }

image-20240421105500261

1.1.2 空值处理

在索引中的一部分文档很可能其某些字段是缺失的,在介绍空值处理前,首先介绍ES聚合查询提供的value_count聚合,该聚合用于统计字段非空值的个数。


bash

复制代码

 # value_count聚合统计price字段中非空值的个数  GET /hotel_poly/_search  {    "size": 0,    "aggs":{      "my_agg":{        "value_count": {          "field": "price"       }     }   }  }

image-20240421105741226

通过上述结果可以看到,当前索引中price字段中的非空值有4个。

以下代码演示了在Java中使用value_count对price字段进行聚合的逻辑。


ini

复制代码

  public void getValueCountAggSearch() throws IOException{          //创建搜索请求          SearchRequest searchRequest = new SearchRequest("hotel_poly");          SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();          String aggName="my_agg";   //聚合的名称          //定义avg聚合,指定字段为price          ValueCountAggregationBuilder aggregationBuilder = AggregationBuilders.count(aggName).field("price");          searchSourceBuilder.aggregation(aggregationBuilder);    //添加聚合          searchRequest.source(searchSourceBuilder);  //设置查询请求          //执行查询          SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);          //获取聚合结果          Aggregations aggregations = searchResponse.getAggregations();          //获取value_count聚合返回的对象          ValueCount valueCount = aggregations.get(aggName);          String key = valueCount.getName();          long count = valueCount.getValue();          log.info("key={},count={}",key,count);     }

image-20240421110011904

需要说明的是,如果判断的字段是数组类型,则value_count统计的是符合条件的所有文档中该字段数组中非空元素个数的总和,而不是数组的个数总和

下面的DSL用于统计tags字段数组中非空元素个数的总和。


bash

复制代码

 #统计tags字段数组钟非空元素个数的总和  GET /hotel_poly/_search  {    "size": 0,    "aggs":{      "my_agg":{        "value_count": {          "field": "tags"       }     }   }  }

在索引的5个文档中,除去文档003没有tags字段外,其他4个文档的tags字段数组中各有两个元素,因此聚合的值为2×4=82×4=8个,ES返回结果如下:

image-20240421110404683

上面的结果中,aggregations.my_agg.value的值为8,这和前面计算的数值相等,验证了使用value_count对数组字段进行聚合时,ES返回的结果是所有数组元素的个数总和。

如果需要以空值字段的数据作为聚合指标对其进行聚合,可以在指标统计中通过missing参数指定填充值对空值进行填充。

以下示例演示了对price字段进行聚合,并设定了当字段值为空值时使用100进行替代的DSL。


arduino

复制代码

# missing参数指定填充值对空值进行填充 GET /hotel_poly/_search { "size": 0, "aggs": { "my_agg": { "sum":{ "field": "price", "missing": 100 //计算加和值时将price字段中的空值用100代替 } } } }

在索引中,文档002的price字段为空,因此被填充为100,文档001、003、004和005的price字段分别为556、200、500和800,因此符合聚合的值应该是556+100+200+500+800=2156556+100+200+500+800=2156。ES返回结果如下:

image-20240421111203396

以下代码演示了在Java中当聚合指标为空值时指定填充值的逻辑。


ini

复制代码

@Test public void getSumAggSearch() throws IOException{ //创建搜索请求 SearchRequest searchRequest = new SearchRequest("hotel_poly"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); String aggName="my_agg"; //聚合的名称 //定义avg聚合,指定字段为price SumAggregationBuilder aggregationBuilder = AggregationBuilders.sum(aggName).field("price"); aggregationBuilder.missing("100"); searchSourceBuilder.aggregation(aggregationBuilder); //添加聚合 searchRequest.source(searchSourceBuilder); //设置查询请求 //执行查询 SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); //获取聚合结果 Aggregations aggregations = searchResponse.getAggregations(); //获取value_count聚合返回的对象 Sum sum = aggregations.get(aggName); String key=sum.getName(); double value = sum.getValue(); log.info("key={},value={}",key,value); }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值