功能简介
为了实现商品搜索时,动态展示分类筛选条件,需要借助聚合查询实现。
功能上有分页、搜索、字段聚合。
难点
做聚合查询时,商品都会有分类、品牌,可以直接使用agg函数查出,但商品的属性是动态变化的,需要进行多次聚合查询,将商品的属性转化为k-v形式(k-属性名,v-属性值集合)。特别是通过java取出时,也需要对数据进行进一步处理。
DSL语法
GET /goods/doc/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"brandName": {
"value": "箭牌"
}
}
},
{
"term": {
"goodsCatId3Name.keyword": {
"value": "合资品牌"
}
}
}
],
"filter": {
"range": {
"linePrice": {
"gte": 10,
"lte": 200
}
}
}
}
},
"size": 1,
"aggs": {
"brands": {
"terms": {
"field": "brandName"
}
},
"categories3":{
"terms": {
"field": "goodsCatId3Name.keyword",
"size": 10
}
},
"searchList_agg":{
"nested": {
"path": "searchList"
},
"aggs": {
"searchListName": {
"terms": {
"field": "searchList.name",
"size": 10
},
"aggs": {
"searchListNameValue": {
"terms": {
"field": "searchList.value",
"size": 10
}
}
}
}
}
}
}
}
响应结果:
"aggregations": {
"brands": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "箭牌",
"doc_count": 6
}
]
},
"categories3": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "合资品牌",
"doc_count": 6
}
]
},
"searchList_agg": {
"doc_count": 6,
"searchListName": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "合资品牌1",
"doc_count": 6,
"searchListNameValue": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "123",
"doc_count": 5
},
{
"key": "456",
"doc_count": 1
}
]
}
}
]
}
}
}
java客户端操作
代码片段
Aggregations aggregations = searchResponse.getAggregations();
//筛选条件
List<Map> conditions = aggregations.asList().stream().map(GoodsIndexServiceImpl::apply).collect(Collectors.toList());
map.put("conditions", conditions);
log.info("conditions->"+conditions);
// 调用的静态方法
//aggregation convert
private static Map apply(Aggregation aggregation) {
Map map = new HashMap();
if (aggregation instanceof ParsedStringTerms) {
String name = aggregation.getName();
List<ParsedStringTerms.ParsedBucket> collect = (List<ParsedStringTerms.ParsedBucket>) ((ParsedStringTerms) aggregation).getBuckets();
List<String> stringList = collect.stream().map(a -> {
String value = a.getKeyAsString() + "|" + a.getDocCount();
return value;
}).collect(Collectors.toList());
map.put(name, stringList);
}
if (aggregation instanceof ParsedNested) {
Aggregations aggregations = ((ParsedNested) aggregation).getAggregations();
for (Aggregation aggregation1 : aggregations) {
List<ParsedStringTerms.ParsedBucket> buckets = (List<ParsedStringTerms.ParsedBucket>) ((ParsedStringTerms) aggregation1).getBuckets();
for (ParsedStringTerms.ParsedBucket bucket : buckets) {
//searchKey
String keyAsString = bucket.getKeyAsString();
Aggregations aggregations1 = bucket.getAggregations();
for (Aggregation aggregation2 : aggregations1) {
List<ParsedStringTerms.ParsedBucket> buckets1 = (List<ParsedStringTerms.ParsedBucket>) ((ParsedStringTerms) aggregation2).getBuckets();
//searchValueList
List<String> valueList = buckets1.stream().map(parsedBucket ->
parsedBucket.getKeyAsString() + "|" + parsedBucket.getDocCount()).collect(Collectors.toList());
map.put(keyAsString, valueList);
}
}
}
}
return map;
}
处理后的返回值:
Json格式
[
{
"brands": [
"箭牌|6",
"3ce|2",
"华为|1",
"小米|1"
]
},
{
"categories3": [
"合资品牌|13"
]
},
{
"合资品牌1": [
"123|11",
"456|1",
"我去二|1"
],
"合资品牌6": [
"你好|1"
]
}
]