很多时候业务上需要分组排序分页的场景,类似于mysql的group by xxx limit 0 10。
so,当数据同步到es后,相同的需求场景也出现了。
背景:商品根据商品销量排序,销量数据是以sku存储的,商品列表展示spu。
实现方式有两种:

思路一:根据sku销量排序,分页,业务上不是很精准==>sort:根据sale_volume销量排序,collapse:根据spuId去重得到去重后的记录,配合"from": 0, "size": 10分页得到结果,cardinality:根据spuId得到去重统计结果,即列表spu数据的总数total。

思路二:根据spu销量排序,分页,业务上精准(相当于先计算spu销量,再排序分页)==> bucket_sort .有一些局限性,见文章底部分析。

直接上DSL

1、准备测试数据:

插入spu商品数据

POST /t_spu_001/_bulk
{"index":{}}
{"id":1508769405482586000,"org_code":"999999","spu_id":1508777903532560400,"sku_id":1508777903570309000,"shop_id":111111,"first_category_id":1508766351106527200,"second_category_id":1508766404416131000,"third_category_id":1508767024225210400,"keywords":null,"spu_name":"弱碱性苏打水娃哈哈苏打水350ml*24瓶整箱 甜味/无味/薄荷味弱碱性苏打水 柠檬味 350ml","sku_attribute":"娃哈哈苏打水350ml*1瓶","main_pic":"https://ruyishangcheng.oss-cn-shanghai.aliyuncs.com/sku-test/2022-03-29/2022-03-29T19:56:26.972/Wbo1k5wD_oTdeMqexkVP7g.jpg","publish_status":1,"price":5,"create_user":"1508762466629263362","create_time":1648553633000,"update_user":"1508762466629263362","update_time":1655802396000}
{"index":{}}
{"id":1508794393874944000,"org_code":"999999","spu_id":1508776205556666400,"sku_id":1508776205577638000,"shop_id":111111,"first_category_id":1508766351106527200,"second_category_id":1508766404416131000,"third_category_id":1508766664723026000,"keywords":null,"spu_name":"娃哈哈饮用纯净水4.5L(1*4聪明盖)(中心自提)","sku_attribute":"4.5L*4瓶","main_pic":"https://ruyishangcheng.oss-cn-shanghai.aliyuncs.com/sku-test/2022-03-29/2022-03-29T19:37:36.579/QQ截图20220328144035.jpg","publish_status":1,"price":3,"create_user":"1508761063194173441","create_time":1648559590000,"update_user":"1508761063194173441","update_time":1655968379000}
{"index":{}}
{"id":1508794478406946800,"org_code":"999999","spu_id":1508771759904804900,"sku_id":1508771759967719400,"shop_id":111111,"first_category_id":1508766351106527200,"second_category_id":1508766404416131000,"third_category_id":1508767081859141600,"keywords":null,"spu_name":"娃哈哈启力功能饮料启力维生素运动功能饮料250ml*24瓶(中心自提)","sku_attribute":"250ml*24瓶","main_pic":"https://ruyishangcheng.oss-cn-shanghai.aliyuncs.com/sku-test/2022-03-29/2022-03-29T19:57:22.095/QQ截图20220324204129.jpg","publish_status":1,"price":2,"create_user":"1508761063194173441","create_time":1648559610000,"update_user":"1508761063194173441","update_time":1655968377000}
{"index":{}}
{"id":1508794557406662700,"org_code":"999999","spu_id":1508771759904804900,"sku_id":1508771759959330800,"shop_id":111111,"first_category_id":1508766351106527200,"second_category_id":1508766404416131000,"third_category_id":1508767180966350800,"keywords":null,"spu_name":"娃哈哈非常可乐碳酸饮料530ml*12瓶 春晚同款(中心自提)","sku_attribute":"530ml*6瓶","main_pic":"https://ruyishangcheng.oss-cn-shanghai.aliyuncs.com/sku-test/2022-03-29/2022-03-29T20:04:50.007/QQ截图20220324132845.jpg","publish_status":1,"price":1,"create_user":"1508761063194173441","create_time":1655968441000,"update_user":"1508761063194173441","update_time":1655968441000}

插入销量库存数据

POST /t_stock_001/_bulk
{"index":{}}
{"id":1508777903672737800,"spu_id":1508777903532560400,"sku_id":1508777903570309000,"sku_code":"K00000011","shop_id":111111,"sale_volume":0,"stock":35,"create_time":1648555659000,"create_user":999999,"update_time":1658892138000,"update_user":999999,"org_code":"999999"}
{"index":{}}
{"id":1508778549868183600,"spu_id":1508776205556666400,"sku_id":1508776205577638000,"sku_code":"K00000010","shop_id":111111,"sale_volume":0,"stock":60,"create_time":1648555813000,"create_user":1508772831021121500,"update_time":1658892138000,"update_user":999999,"org_code":"999999"}
{"index":{}}
{"id":1508778707439796200,"spu_id":1508771759904804900,"sku_id":1508771759959330800,"sku_code":"K00000006","shop_id":111111,"sale_volume":10,"stock":55,"create_time":1648555850000,"create_user":1508772831021121500,"update_time":1658892138000,"update_user":999999,"org_code":"999999"}
{"index":{}}
{"id":1508778707628540000,"spu_id":1508771759904804900,"sku_id":1508771759967719400,"sku_code":"K00000008","shop_id":111111,"sale_volume":1,"stock":20,"create_time":1648555850000,"create_user":1508772831021121500,"update_time":1658892138000,"update_user":999999,"org_code":"999999"}
{"index":{}}
{"id":1509413147273220000,"spu_id":1508771759904804900,"sku_id":1508771759959330800,"sku_code":"K00000006","shop_id":111111,"sale_volume":10,"stock":991,"create_time":1648707113000,"create_user":1508762466629263400,"update_time":1658892138000,"update_user":999999,"org_code":"999999"}
{"index":{}}
{"id":1508784994321375200,"spu_id":1508784994041221000,"sku_id":1508784994078969900,"sku_code":"K00000014","shop_id":111111,"sale_volume":0,"stock":9998,"create_time":1648557349000,"create_user":999999,"update_time":1658892138000,"update_user":999999,"org_code":"999999"}

1、查出门店id为111111的门店下所有上架的在售商品

POST t_spu_001/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"shop_id": {
"value": 111111
}
}
},
{
"term": {
"publish_status": {
"value": 1
}
}
}
]
}
},
"collapse": {
"field": "spu_id"
},
"aggs": {
"total_spu": {
"cardinality": {
"field": "spu_id"
}
}
}
}
total_spu就是商品列表总数。

2.1、(思路一实现方式)再根据步骤1查出来的spu和门店从销量库存表根据销量排序分页查询商品列表

利用es折叠collapse,近似聚合cardinality(类似distinct )实现。

POST /t_stock_001/_search
{
"size": 10,
"query": {
"bool": {
"filter": [
{
"terms": {
"spu_id": [
"1508777903532560400",
"1508776205556666400"
]
}
},
{
"term": {
"shop_id": {
"value": 111111
}
}
}
]
}
},
"sort": [
{
"sale_volume": {
"order": "desc"
}
}
],
"collapse": {
"field": "spu_id"
},
"aggs": {
"total_spu": {
"cardinality": {
"field": "spu_id"
}
}
}
}

java代码:

 1 NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
2 BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
3 if (CollectionUtils.isNotEmpty(spuIds)) {
4 boolQueryBuilder.filter(QueryBuilders.termsQuery("spu_id", spuIds));
5 }
6 if (Objects.nonNull(shopId)) {
7 boolQueryBuilder.filter(QueryBuilders.termQuery("shop_id", shopId));
8 }
9 builder.withQuery(boolQueryBuilder);
10 //去重
11 builder.withCollapseField("spu_id");
12 //取去重后的count(大数据量下有准确性和性能问题):precision_threshold默认4000,4000以内可确保100%准确性
13 builder.withAggregations(AggregationBuilders.cardinality("total_spu").field("spu_id"));
14
15 //排序(销量)
16 builder.withSorts(Collections.singleton(SortBuilders.fieldSort("sale_volume").order(SortOrder.DESC)));
17
18 //分页
19 builder.withPageable(PageRequest.of(dto.getPage() - 1, dto.getPageSize()));
20 builder.withTrackScores(true);
21 NativeSearchQuery searchQuery = builder.build();

2.2、(思路二实现方式)再根据步骤1查出来的spu和门店从销量库存表根据销量排序分页查询商品列表

利用es聚合桶排序bucket_sort实现。

如果需要限制商品列表最多展示多少屏,则使用最大页数限制。

POST /t_stock_001/_search
{
"size": 10,
"query": {
"bool": {
"filter": [
{
"terms": {
"spu_id": [
"1508777903532560400",
"1508776205556666400"
]
}
},
{
"term": {
"shop_id": {
"value": 111111
}
}
}
]
}
},
"aggs": {
"spu_id": {
"terms": {
"field": "spu_id",
"size": 1000
},
"aggs": {
"spuCount": {
"sum": {
"field": "sale_volume"
}
},
"selfSort": {
"bucket_sort": {
"sort": [{
"spuCount": "asc"
}],
"from": 0,
"size": 6
}
}
}
}
}
}

java代码:

 1 NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
2 BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
3 if (CollectionUtils.isNotEmpty(spuIds)) {
4 boolQueryBuilder.filter(QueryBuilders.termsQuery("spu_id", spuIds));
5 }
6 if (Objects.nonNull(shopId)) {
7 boolQueryBuilder.filter(QueryBuilders.termQuery("shop_id", shopId));
8 }
9
10 productStockBuilder.withQuery(boolQueryBuilder);
11 //排序:按销量
12 productStockBuilder.withAggregations(AggregationBuilders.terms("spu").field("spu_id")
13 .size(1000)
14 .shardSize(1)
15 .subAggregation(AggregationBuilders.sum("spuCount").field("sale_volume"))
16 .subAggregation(new BucketSortPipelineAggregationBuilder("spu_bucket_sort",
17 Collections.singletonList(new FieldSortBuilder("spuCount").unmappedType("long").order(SortOrder.DESC)))
18 .from(page - 1)
19 .size(pageSize)));
20
21 NativeSearchQuery searchQuery = builder.build();
22 searchQuery.setTrackTotalHits(true);

官方文档

collapse + cardinality 说明:

1、collapse:去重得到去重后的记录,配合"from": 0, "size": 1分页得到结果

2、cardinality:得到去重统计结果

bucket_sort部分解释:

  • 最外层的size=0,表示该查询不返回详情,只返回聚合结果;
  • query中使用一个must列表对数据进行过滤;
  • terms实现分桶的功能,类似于sql中的分组功能;
  • terms中的shard_size表示每个分片返回的数据量,size表示返回的桶的数据,会收到bucket_sort中size的限制;
  • value_count实现计数的一个功能;
  • sort指定排序的字段和排序的升降序,可以使用聚合后的字段;
  • 使用bucket_sort的功能,from、size分别表示从第几条数据开始,取多少条数据。

特别注意:

  • 在terms中使用bucket_sort功能的时候,terms中分组的size大小设置应该大于bucket_sort中的from+size的大小,否则会因为terms中size的大小限制了返回的数据。
  • bucket_sort的sort排序是针对父聚合返回的结果进行排序的,比如上述terms返回的结果为1000条,那么bucket_sort仅对这1000条进行排序。

elasticsearch聚合桶排序、分页实战的更多相关文章

  1. elasticsearch聚合--桶(Buckets)和指标(Metrics)的概念

    写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------主要内容包括: 聚合的两个核 ...

  2. ElasticSearch聚合(转)

    ES之五:ElasticSearch聚合 前言 说完了ES的索引与检索,接着再介绍一个ES高级功能API – 聚合(Aggregations),聚合功能为ES注入了统计分析的血统,使用户在面对大数据提 ...

  3. ElasticSearch聚合

    前言 说完了ES的索引与检索,接着再介绍一个ES高级功能API – 聚合(Aggregations),聚合功能为ES注入了统计分析的血统,使用户在面对大数据提取统计指标时变得游刃有余.同样的工作,你在 ...

  4. Elasticsearch 聚合统计与SQL聚合统计语法对比(一)

    Es相比关系型数据库在数据检索方面有着极大的优势,在处理亿级数据时,可谓是毫秒级响应,我们在使用Es时不仅仅进行简单的查询,有时候会做一些数据统计与分析,如果你以前是使用的关系型数据库,那么Es的数据 ...

  5. ElasticSearch聚合分析

    聚合用于分析查询结果集的统计指标,我们以观看日志分析为例,介绍各种常用的ElasticSearch聚合操作. 目录: 查询用户观看视频数和观看时长 聚合分页器 查询视频uv 单个视频uv 批量查询视频 ...

  6. ElasticSearch 聚合分析

    公号:码农充电站pro 主页:https://codeshellme.github.io ES 中的聚合分析(Aggregations)是对数据的统计分析功能,它的优点是实时性较高,相比于 Hadoo ...

  7. ElasticSearch 聚合函数

    一.简单聚合 桶 :简单来说就是满足特定条件的文档的集合. 指标:大多数 指标 是简单的数学运算(例如最小值.平均值.最大值,还有汇总),这些是通过文档的值来计算. 桶能让我们划分文档到有意义的集合, ...

  8. Mysql 单表查询-排序-分页-group by初识

    Mysql 单表查询-排序-分页-group by初识 对于select 来说, 分组聚合(((group by; aggregation), 排序 (order by** ), 分页查询 (limi ...

  9. [SQL] SQL 基础知识梳理(三) - 聚合和排序

    SQL 基础知识梳理(三) - 聚合和排序 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5926689.html 序 这是<SQL 基础知识梳理 ...

  10. 计数排序和桶排序(Java实现)

    目录 比较和非比较的区别 计数排序 计数排序适用数据范围 过程分析 桶排序 网络流传桶排序算法勘误 桶排序适用数据范围 过程分析 比较和非比较的区别 常见的快速排序.归并排序.堆排序.冒泡排序等属于比 ...

随机推荐

  1. NC15033 小G有一个大树

    题目链接 题目 题目描述 小G想要把自己家院子里的橘子树搬到家门口(QAQ..就当小G是大力水手吧) 可是小G是个平衡性灰常灰常差的人,他想找到一个这个橘子树的平衡点. 怎么描述这棵树呢...就把它看 ...

  2. TS内置类型与拓展

    TS内置类型与拓展 TypeScript具有类型系统,且是JavaScript的超集,其可以编译成普通的JavaScript代码,也就是说,其是带有类型检查的JavaScript. 内置类型 Type ...

  3. Git实战系列教程

    介绍 本文详细记录了Git一系列核心概念和工作中常用的操作命令,通篇以实际出发拒绝过度理论,值得典藏:). 概念 版本管理系统 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的 ...

  4. 网络通信部分之bs/cs架构,网络概念,osi七层网络模型,TCP/UDP协议---day27

    1.网络开发的两大架构c/s,b/s # ### 1.网络开发的两大架构 a文件 -> b文件 借助c文件 a文件和b文件进行数据交流,借助c文件中转数据 a文件把数据放在c文件中,b文件从c文 ...

  5. python中partial用法

    应用 典型的,函数在执行时,要带上所有必要的参数进行调用.然后,有时参数可以在函数被调用之前提前获知.这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用. 示例pyqt5 ...

  6. ASP.NET Core 微信支付(一)【统一下单 APIV3】

    官方参考资料 签名:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml 签名生成:https://wechatp ...

  7. 【应用服务 App Service】在Azure Web App的部署文件中,是否可以限制某些文件无法被访问?(如json)

    问题描述 当部署文件到Azure App Service上后,默认访问文件在wwwroot目录中,如appsettings.json文件,在通过URL+文件名的形式可以访问,这样敏感信息会被泄露出去, ...

  8. window.open代理劫持

    window.open = new Proxy(window.open, { apply(target, ctx, args) { if (hasAuth(args[0])) { return tar ...

  9. 如何获取拼多多推流码并使用OBS进行直播-疯狂URL

    简介 拼多多直播在PC端可以用多多视频|多多直播端进行开播,它的功能类似于常见的抖音直播助手和快手直播伴侣等等客户端.此教程测试时间 2023-7-12,第三方随时可能会升级,无法保证时效,建议不要升 ...

  10. APISIX的安装和简单使用

    APISIX 是一个云原生.高性能.可扩展的微服务 API 网关. 它是基于 Nginx 和 etcd 来实现,和传统 API 网关相比,APISIX 具备动态路由和插件热加载,特别适合微服务体系下的 ...