ES聚合常见的有三类:
桶(Bucket):用来对文档做分组
TermAggregation:按照文档字段值分组,例如按照品牌值分组、按照城市分组
Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组
度量(Metric):用以计算一些值,比如:最大值、最小值、平均值等
Avg:求平均值
Max:求最大值
Min:求最小值
Stats:同时求max、min、avg、sum等
管道(pipeline):其它聚合的结果为基础做聚合
注意:参加聚合的字段必须是keyword、日期、数值、布尔类型
- Bucket聚合语法
现在,我们要统计所有数据中的酒店品牌有几种,此时可以根据酒店品牌的名称做聚合。类型为term类型,DSL示例:
-- 相当于mysql
select brand,count(*) as num from tb_hotel group by brand
GET /hotel/_search
{
"size": 0,//是不查询原始文档
"aggs": {//聚合函数AGG
"brandAgg": {//自己起的名字对品牌的聚合
"terms": {//根据词条精准查询
"field": "brand",//查询品牌
"size": 20,//每页20
"order": {
"_count": "asc"//由低到高
}
}
}
}
}
- Metric聚合语法
现在我们需要对桶内的酒店做运算,获取每个品牌的用户评分的min、max、avg等值。
这就要用到Metric聚合了,例如stat聚合:就可以获取min、max、avg等结果。
GET /hotel/_search
{
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20
},
"aggs": { // 是brands聚合的子聚合,也就是分组后对每组分别计算
"scoreAgg": { // 聚合名称
"stats": { // 聚合类型,这里stats可以计算min、max、avg等
"field": "score" // 聚合字段,这里是score
}
}
}
}
}
}
-- 相当于mysql
select brand,count(*) as num,sum(price),avg(price),max(price),min(price) from tb_hotel group by brand order by num
知识小结
aggs代表聚合,与query同级,此时query的作用是?
限定聚合的的文档范围
聚合必须的三要素:
聚合名称
聚合类型
聚合字段
聚合可配置属性有:
size:指定聚合结果数量
order:指定聚合结果排序方式
field:指定聚合字段
—————————————————————————————————————
- API语法
我们以品牌聚合为例,演示下Java的RestClient使用,先看请求组装:
上面的是逻辑,现在开始实操
—————————————————————————————————————
返回值类型是一个Key value类型的 MAP集合Map<String,List<String>>
key是字符串,城市、星级、品牌、价格
value是集合,例如多个城市的名称
IHotelService
Map<String, List<String>> filters(RequestParams params) throws IOException;
HotelController
@PostMapping("/filters")
public Map<String, List<String>> getFilters(@RequestBody RequestParams params) throws IOException {
return hotelService.filters(params);
}
HotelService
@Override
public Map<String, List<String>> filters(RequestParams params) throws IOException {
// 1.创建request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL参数
// 2-1 query
buildBasicQuery(params, request);//此处构建了一个buildBasicQuery方法进行DSL拼接
// 2-2 设置size
request.source().size(0);
// 2-3 聚合//链式编程
request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(50));
request.source().aggregation(AggregationBuilders.terms("cityAgg").field("city").size(50));
request.source().aggregation(AggregationBuilders.terms("starNameAgg").field("starName").size(50));
// 3.发出请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
Map<String, List<String>> result = new HashMap<>();
Aggregations aggregations = response.getAggregations();
// 4-1 根据品牌名称
List<String> brandAgg = getAggByName(aggregations, "brandAgg");
result.put("brand", brandAgg);
// 4-2 根据城市名称
List<String> cityAgg = getAggByName(aggregations, "cityAgg");
result.put("city", cityAgg);
// 4-3 根据星级名称
List<String> starNameAgg = getAggByName(aggregations, "starNameAgg");
result.put("starName", starNameAgg);
return result;
}
private List<String> getAggByName(Aggregations aggregations, String aggName) {
// 根据名称获取聚合结果
Terms terms = aggregations.get(aggName);
// 获取bucktes
List<? extends Terms.Bucket> buckets = terms.getBuckets();
// 遍历
List<String> list = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
// 获取聚合名称(key)
String key = bucket.getKeyAsString();
// 添加集合
list.add(key);
}
// 返回
return list;
}
private void buildBasicQuery(RequestParams params, SearchRequest request) {
//2-1 query拼接dsl
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//关键字搜索
String key = params.getKey();
if (key!=null|| "".equals(key) ){
boolQuery.must(QueryBuilders.matchAllQuery());
}else{
boolQuery.must(QueryBuilders.matchQuery("all",key));
}
//城市 过滤
if (StrUtil.isNotEmpty(params.getCity())){
boolQuery.filter(QueryBuilders.termQuery("city",params.getCity()));
}
//品牌过滤
if (StrUtil.isNotEmpty(params.getBrand())){
boolQuery.filter(QueryBuilders.termQuery("brand",params.getBrand()));
}
//星级过滤
if (StrUtil.isNotEmpty(params.getStarName())) {
boolQuery.filter(QueryBuilders.termQuery("starName",params.getStarName()));
}
//价格区间
if (params.getMinPrice()!=null&¶ms.getMaxPrice()!=null){
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
}
//2.算分控制
FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(boolQuery, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
QueryBuilders.termQuery("isAD", true),
ScoreFunctionBuilders.weightFactorFunction(10)
)
});
// 3.放入 source
request.source().query(functionScoreQuery);
}