一、数据聚合
1.1 聚合的分类
聚合可以实现对文档数据的统计、分析、运算(MySQL中的聚合函数有avg,max,min,sum,一般还要结合group by 分组使用)。常见的有三类: (1)桶(Bucket)聚合:用来对文档做分组。 TermAggregation:按文档字段值(该字段不能分词,不能是text)分组 Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组 (2)度量(Metric)聚合:用以计算一些值,比如最大值、最小值、平均值 Avg:求平均值 Max:求最大值 Min:求最小值 Stats:同时求max、min、avg、sum等。 (3)管道(pipeline)聚合:其它聚合的结果为基础做聚合
1.2 DSL实现
还是博文springcloud篇7中添加的hotel索引:
# 酒店的mapping
PUT /hotel
{
\”mappings\”:{
\”properties\”:{
\”id\”:{
\”type\”:\”keyword\”
},
\”name\”:{
\”type\”:\”text\”,
\”analyzer\”:\”ik_max_word\”,
\”copy_to\”:\”all\”
},
\”address\”:{
\”type\”:\”keyword\”,
\”index\”:false
},
\”price\”:{
\”type\”:\”integer\”
},
\”score\”:{
\”type\”:\”integer\”
},
\”brand\”:{
\”type\”:\”keyword\”,
\”copy_to\”:\”all\”
},
\”city\”:{
\”type\”:\”keyword\”
},
\”starName\”:{
\”type\”:\”keyword\”
},
\”business\”:{
\”type\”:\”keyword\”,
\”copy_to\”:\”all\”
},
\”location\”:{
\”type\”:\”geo_point\”
},
\”pic\”:{
\”type\”:\”keyword\”,
\”index\”:false
},
\”all\”:{
\”type\”:\”text\”,
\”analyzer\”:\”ik_max_word\”
}
}
}
}
1.2.1 实现Bucket聚合
例子:统计所有数据中的酒店品牌有几种,此时可以根据酒店品牌的名称做聚合。


# 聚合功能
GET /hotel/_search
{
\”size\”:0,
\”aggs\”:{
\”brandAgg\”:{
\”terms\”:{
\”field\”:\”brand\”,
\”size\”:10
}
}
}
}
补充1.Bucket聚合-聚合结果排序
默认情况下,Bucket聚合会统计Bucket内的文档数量,记为_count,并且按照_count降序排序。 可以修改结果排序方式:

# 聚合功能,自定义排序规则
GET /hotel/_search
{
\”size\”:0,
\”aggs\”:{
\”brandAgg\”:{
\”terms\”:{
\”field\”:\”brand\”,
\”size\”:20,
\”order\”:{
\”_count\”:\”asc\”
}
}
}
}
}
补充2.Bucket聚合-限定聚合范围
默认情况下,Bucket聚合是对索引库的所有文档做聚合,可以限定要聚合的文档范围,只要添加query条件即可。

# 聚合功能,限定聚合范围
GET /hotel/_search
{
\”query\”:{
\”range\”:{
\”price\”:{
\”lte\”:200
}
}
},
\”size\”:0,
\”aggs\”:{
\”brandAgg\”:{
\”terms\”:{
\”field\”:\”brand\”,
\”size\”:20,
\”order\”:{
\”_count\”:\”asc\”
}
}
}
}
}
1.2.2 实现Metric聚合
需与bucket聚合结合使用。 例子,获取每个品牌的用户评分的min、max、avg等值。
从上图中可以看出,聚合可以嵌套,brandAgg聚合内部嵌套了一个score_stats聚合。 
# 嵌套聚合metric
GET /hotel/_search
{
\”size\”:0,
\”aggs\”:{
\”brandAgg\”:{
\”terms\”:{
\”field\”:\”brand\”,
\”size\”:20
},
\”aggs\”:{
\”scoreAgg\”:{
\”stats\”:{
\”field\”:\”score\”
}
}
}
}
}
}
再根据聚合的计算结果排序: 
# 嵌套聚合metric
GET /hotel/_search
{
\”size\”:0,
\”aggs\”:{
\”brandAgg\”:{
\”terms\”:{
\”field\”:\”brand\”,
\”size\”:20,
\”order\”:{
\”scoreAgg.avg\”:\”desc\”
}
},
\”aggs\”:{
\”scoreAgg\”:{
\”stats\”:{
\”field\”:\”score\”
}
}
}
}
}
}
1.3 RestAPI实现聚合
获取查询结果:
聚合结果解析:

@Test
void testAggregation() throws IOException {
//1.创建Request对象
SearchRequest request=new SearchRequest(\”hotel\”);
//2.准备DSL语句
//2.1 设置size
request.source().size(0);
//2.2 聚合
request.source().aggregation(AggregationBuilders.terms(\”brandAgg\”).field(\”brand\”).size(10));
//3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
System.out.println(response);
//4.解析结果
Aggregations aggregations = response.getAggregations();
//4.1根据聚合名称获取聚合结果
Terms brandTerms = aggregations.get(\”brandAgg\”);
//4.2 获取buckets
List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
//4.3 遍历
for(Terms.Bucket bucket : buckets){
String key = bucket.getKeyAsString();
System.out.println(key);
}
}

1.4 多条件聚合
如上图所示,前端页面的筛选条件(城市、星级、品牌和价格)的选项并不是前端写死的,而是要通过后台搜索聚合结果得到。 
public interface IHotelService extends IService<Hotel> {
PageResult search(RequestParams params);
Map<String, List<String>> filters();
}

@Override
public Map<String, List<String>> filters() {
try {
//1.创建Request对象
SearchRequest request=new SearchRequest(\”hotel\”);
//2.准备DSL语句
//2.1 设置size
request.source().size(0);
//2.2 聚合
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));
//3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
Map<String, List<String>> result = new HashMap<>();
Aggregations aggregations = response.getAggregations();
List<String> brandList = getAggByName(aggregations,\”brandAgg\”);
result.put(\”品牌\”, brandList);
List<String> cityList = getAggByName(aggregations,\”cityAgg\”);
result.put(\”城市\”, cityList);
List<String> starList = getAggByName(aggregations,\”starAgg\”);
result.put(\”星级\”, starList);
return result;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static List<String> 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<String> brandList = new ArrayList<>();
for(Terms.Bucket bucket : buckets




