1 需求分析
最近接到需求,需要对聚合后的商铺信息进行分页查询,同时还要查询商铺下面的商品
大体结构如下
[{ shop1:[ { spuinfo1 }, { spuinfo2 } ] }, { shop1:[ { spuinfo1 }, { spuinfo2 } ] } ]
这种结构可以通过es的Aggregation来实现,还要对结果进行排序及分页。我们需要返回的数据有:聚合后的数据总数,用于前端展示分页信息,以及分页查询的起始位置。
2 实现方案
结果总数可以通过 cardinality类似SQL中distinct某个字段后,再count,于是使用cardinality获取相关聚合结果的total:
用bucket_sort实现分页
在terms中使用bucket_sort功能的时候,terms中分组的size大小设置应该大于bucket_sort中的from+size的大小,否则会因为terms中size的大小限制了返回的数据。bucket_sort的sort排序是针对父聚合返回的结果进行排序的,比如上述terms返回的结果为1000条,那么bucket_sort仅对这1000条进行排序。
3 query 查询
GET /productinfos/_search { "aggs": { "shoppes": { "terms": { "field": "shoppeId", "order": { "shoppe_sort": "desc" }, "size": 4 }, "aggs": { "shop_sort": { "sum": { "field": "salecount" } }, "shoppe_spu": { "order": { "max": "spuid" } }, "shop_bucket": { "bucket_sort": { "from": 0, "size": 5, "gap_policy": "SKIP" } } } } }, "size": 0 }
4 代码实现
Map<String, SortOrder> orderMap2 = new HashMap<>();
orderMap2.put("salecount_sort",SortOrder.Desc);
Aggregation salecount_sort = Aggregation.of(a -> a.max(v -> v.field(param.getOrder())));
int bucketFrom = (param.getPageNum()-1)*param.getPageSize();
int bucketSize = bucketFrom + param.getPageSize();
Aggregation shoppes = Aggregation.of(
a -> a.terms(v -> v.field("shoppeId").size(bucketSize).order(orderMap))
.aggregations("shop_sort", s -> param.getOrder().equals("salecount")? s.sum(v -> v.field(param.getOrder())):s.max(v -> v.field(param.getOrder())))
.aggregations("shop_bucketSort", b ->b.bucketSort(s -> s.from(bucketFrom).size(param.getPageSize()).gapPolicy(GapPolicy.Skip)))
.aggregations("shop_spu", b -> b.terms(v -> v.field("spuId").size(param.getSpuNum()).order(orderMap2))
.aggregations("salecount_sort",salecount_sort)));
Aggregation shoppeCount = Aggregation.of(c -> c.cardinality(v -> v.field(SearchParamConfig.SHOPPEID)));