07-数据聚合-带条件过滤的聚合
1.对接前端接口
前端页面会向服务端发起请求,查询品牌、城市、星级等字段的聚合结果:
可以看到请求参数与之前search时的RequestParam完全一致,这是在限定聚合时的文档范围。
例如:用户搜索“外滩”,价格在300~600,那聚合必须是在这个搜索条件基础上完成。
因此我们需要:
编写controller接口,接收该请求
修改IUserService#getFilters()方法,添加RequestParam参数
修改getFilters方法的业务,聚合时添加query条件
前端点击搜索。发起了两个请求,一个是list,一个是filter。filters是查询酒店过滤项的请求。
会发现这个请求和list请求的请求参数是一样的。为什么我们在查过滤项的时候要带条件呢?
过滤项查询将来要通过聚合来实现,而聚合一旦加了条件,是在限定聚合的范围。为什么聚合要限定范围??为什么不能直接对整个索引库做聚合??
如果在搜索的时候没有加任何条件,那么搜到的是索引库的所有数据,这个时候对索引库的所有数据去做聚合得到城市和品牌有哪些没问题。但是我们现在搜了虹桥,那我得到的结果一定是跟上海虹桥有关的结果,那么这个时候上海虹桥火车站有关的,那他的城市对应的一定是上海,但是你却对索引库的所有数据去做聚合,你得到的城市一定包括了所有城市,上海北京深圳等等。这个时候用户就很奇怪了,搜的虹桥是上海的,为什么还出现北京了?现在如果点击北京,再结合搜索条件却搜不到任何东西。这个时候就出事了,索引不应该对索引库中的所有数据去做聚合。
这个时候用户条件是虹桥,我们就应该对虹桥相关的酒店去做聚合,也就是要限定聚合的范围。怎么限定聚合的范围??
添加查询条件。在查酒店的时候用的是什么条件,在做聚合的时候也要用什么条件。这样就是在酒店结果的基础上去做聚合了。聚合结果就更准确了。因此我们在查询酒店和查询过滤项时要用相同的查询条件。如果是用上节课哪种代码,或发现页面不显示“城市”、“品牌”、“星级”这些信息,按照下面改了就行了。
注意这里把代码中的 【品牌、城市、星级】 分别使用 【brand、city、starName】 替换。如:result.put("brand", brandList);
Java代码
// 获取城市、星级、品牌等信息
@Override
public Map<String, List<String>> filters(RequestParams params) {
try {
// 1.准备请求
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1 query
selectCondition(params, request);
// 2.2 设置size
request.source().size(0);
// 2.3 聚合
buildAggregation(request);
// 3.发出请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4. 结果解析
Map<String, List<String>> result = new HashMap<>();
Aggregations aggregations = response.getAggregations();
// 4.1根据名称获取品牌结果
List<String> brandList = getAggByName(aggregations, "brandAgg");
// 4.2根据名称,获取城市结果
List<String> cityList = getAggByName(aggregations, "cityAgg");
// 4.3根据名称,获取星级结果
List<String> starList = getAggByName(aggregations, "starAgg");
// 4.4 放入map
result.put("brand", brandList);
result.put("city", cityList);
result.put("starName", starList);
return result;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private List<String> getAggByName(Aggregations aggregations, String aggName) {
// 4.1 根据聚合名称获取聚合结果
// 因为存的时候是term类型的,所以取的时候也要变成Terms类型的,是一个接口
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) {
// 4.4 获取key
String key = bucket.getKeyAsString();
brandList.add(key);
}
return brandList;
}
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)
);
}
// 添加DSL语句
private void selectCondition(RequestParams params, SearchRequest request) {
// 2.准备DSL
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 2.1 query
String key = params.getKey();
if (key == null || "".equals(key)) {
boolQuery.must(QueryBuilders.matchAllQuery());
} else {
boolQuery.must(QueryBuilders.matchQuery("all", key));
}
// 2.2 品牌
if (params.getBrand() != null && !params.getBrand().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
// 2.3 城市
if (params.getCity() != null && !params.getCity().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
// 2.4 星级
if (params.getStarName() != null && !params.getStarName().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
// 2.5 价格
if (params.getMinPrice() != null && params.getMaxPrice() != null) {
boolQuery.filter(QueryBuilders
.rangeQuery("price")
.gte(params.getMinPrice())
.lte(params.getMaxPrice()));
}
FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
// 原始查询,相关性算分的查询
boolQuery,
// function score 的数组
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
// 其中的一个function score元素
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
// 过滤条件
QueryBuilders.termQuery("isAD", true),
// 算分函数
ScoreFunctionBuilders.weightFactorFunction(10)
)
}
);
request.source().query(functionScoreQuery);
}