聚合可以让我们极其方便的实现对数据的统计、分析、运算。例如:
什么品牌的手机最受欢迎?
这些手机的平均价格、最高价格、最低价格?
这些手机每月的销售情况如何?
实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现近实时搜索效果
聚合常见的有三类:
桶(Bucket)聚合:用来对文档做分组
TermAggregation:按照文档字段值分组,例如按照品牌值分组、按照国家分组
Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组
度量(Metric)聚合:用以计算一些值,比如:最大值、最小值、平均值等
Avg:求平均值
Max:求最大值
Min:求最小值
Stats:同时求max、min、avg、sum等
管道(pipeline)聚合:其它聚合的结果为基础做聚合
注意:参加聚合的字段必须是keyword、日期、数值、布尔类型
- GET /hotel/_search
- {
- "size": 0,
- "aggs": {
- "brandAgg": {
- "terms": {
- "field": "brand",
- "size": 20
- }
- }
- }
- }
发起请求的结果

默认情况下,Bucket聚合会统计Bucket内的文档数量,记为count,并且按照count降序排序。
我们可以指定order属性,自定义聚合的排序方式,按照_count降序排列
- GET /hotel/_search
- {
- "size": 0,
- "aggs": {
- "brandAgg": {
- "terms": {
- "field": "brand",
- "order": {
- "_count": "desc"
- },
- "size": 20
- }
- }
- }
- }
发起请求的结果, 按照_count降序排列。

默认情况下,Bucket聚合是对索引库的所有文档做聚合,但真实场景下,用户会输入搜索条件,因此聚合必须是对搜索结果聚合。那么聚合必须添加限定条件。我们可以限定要聚合的文档范围,只要添加query条件即可。
只对200元以下的文档聚合
- GET /hotel/_search
- {
- "query": {
- "range": {
- "price": {
- "lte": 200
- }
- }
- },
- "size": 0,
- "aggs": {
- "brandAgg": {
- "terms": {
- "field": "brand",
- "size": 20
- }
- }
- }
- }
聚合得到的品牌明显变少了

现在我们需要对桶内的酒店做运算,获取每个品牌的用户评分的min、max、avg等值
score_stats聚合是在brandAgg的聚合内部嵌套的子聚合。因为我们需要在每个桶分别计算
- GET /hotel/_search
- {
- "size": 0,
- "aggs": {
- "brandAgg": {
- "terms": {
- "field": "brand",
- "size": 20
- },
- "aggs": {
- "score_stats": {
- "stats": {
- "field": "score"
- }
- }
- }
- }
- }
- }
我们还可以给聚合结果做个排序,例如按照每个桶的酒店平均分做排序,score_stats.avg对score聚合函数的平均值进行降序排序。
- GET /hotel/_search
- {
- "size": 0,
- "aggs": {
- "brandAgg": {
- "terms": {
- "field": "brand",
- "size": 20,
- "order": {
- "score_stats.avg": "desc"
- }
- },
- "aggs": {
- "score_stats": {
- "stats": {
- "field": "score"
- }
- }
- }
- }
- }
- }
aggs代表聚合,与query同级
聚合必须的三要素:
聚合名称
聚合类型
聚合字段
聚合可配置属性有:
size:指定聚合结果数量
order:指定聚合结果排序方式
field:指定聚合字段
搜索页面的品牌、城市等信息不应该是在页面写死,而是通过聚合索引库中的酒店数据得来的
-
-
- import cn.itcast.hotel.pojo.PageResult;
- import cn.itcast.hotel.pojo.RequestParams;
- import cn.itcast.hotel.service.IHotelService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestBody;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import java.util.List;
- import java.util.Map;
-
- @RestController
- @RequestMapping("/hotel")
- public class HotelController {
-
- @Autowired
- private IHotelService hotelService;
-
-
- @PostMapping("filters")
- public Map
> getFilters(@RequestBody RequestParams params){ - return hotelService.getFilters(params);
- }
- }
-
-
- import cn.itcast.hotel.mapper.HotelMapper;
- import cn.itcast.hotel.pojo.Hotel;
- import cn.itcast.hotel.pojo.HotelDoc;
- import cn.itcast.hotel.pojo.PageResult;
- import cn.itcast.hotel.pojo.RequestParams;
- import cn.itcast.hotel.service.IHotelService;
- import com.alibaba.fastjson.JSON;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import org.elasticsearch.action.search.SearchRequest;
- import org.elasticsearch.action.search.SearchResponse;
-
- import org.elasticsearch.client.RequestOptions;
- import org.elasticsearch.client.RestHighLevelClient;
- import org.elasticsearch.common.geo.GeoPoint;
- import org.elasticsearch.common.unit.DistanceUnit;
- import org.elasticsearch.index.query.BoolQueryBuilder;
- import org.elasticsearch.index.query.QueryBuilders;
- import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
- import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
- import org.elasticsearch.search.SearchHit;
- import org.elasticsearch.search.SearchHits;
- import org.elasticsearch.search.aggregations.AggregationBuilders;
- import org.elasticsearch.search.aggregations.Aggregations;
- import org.elasticsearch.search.aggregations.bucket.terms.Terms;
- import org.elasticsearch.search.sort.SortBuilders;
- import org.elasticsearch.search.sort.SortOrder;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- @Service
- public class HotelService extends ServiceImpl
implements IHotelService { -
- @Autowired
- private RestHighLevelClient client;
-
-
-
- @Override
- public Map
> getFilters(RequestParams params) { - try {
- // 1.准备Request
- SearchRequest request = new SearchRequest("hotel");
- // 2.准备DSL
- // 2.1.query
- // buildBasicQuery(params, request);
- // 2.2.设置size
- request.source().size(0);
- // 2.3.聚合
- buildAggregation(request);
- // 3.发出请求
- SearchResponse response = client.search(request, RequestOptions.DEFAULT);
- // 4.解析结果
- Map
> result = new HashMap<>(); - Aggregations aggregations = response.getAggregations();
- // 4.1.根据品牌名称,获取品牌结果
- List
brandList = getAggByName(aggregations, "brandAgg"); - result.put("品牌", brandList);
- // 4.2.根据品牌名称,获取品牌结果
- List
cityList = getAggByName(aggregations, "cityAgg"); - result.put("城市", cityList);
- // 4.3.根据品牌名称,获取品牌结果
- List
starList = getAggByName(aggregations, "starAgg"); - result.put("星级", starList);
-
- return result;
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- private void buildAggregation(SearchRequest request) {
- request.source().aggregation(AggregationBuilders
- .terms("brandAgg")
- .field("brand")
- .size(100)
- );
- request.source().aggregation(AggregationBuilders
- .terms("cityAgg")
- .field("city")
- .size(100)
- );
- request.source().aggregation(AggregationBuilders
- .terms("starAgg")
- .field("starName")
- .size(100)
- );
- }
-
- private List
getAggByName(Aggregations aggregations, String aggName) { - // 4.1.根据聚合名称获取聚合结果
- Terms brandTerms = aggregations.get(aggName);
- // 4.2.获取buckets
- List extends Terms.Bucket> buckets = brandTerms.getBuckets();
- // 4.3.遍历
- List
brandList = new ArrayList<>(); - for (Terms.Bucket bucket : buckets) {
- // 4.4.获取key
- String key = bucket.getKeyAsString();
- brandList.add(key);
- }
- return brandList;
- }
-
-
- }
发送请求,获得结果
