elasticsearch聚合桶排序、分页实战
很多时候业务上需要分组排序分页的场景,类似于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聚合桶排序、分页实战的更多相关文章
- elasticsearch聚合--桶(Buckets)和指标(Metrics)的概念
写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------主要内容包括: 聚合的两个核 ...
- ElasticSearch聚合(转)
ES之五:ElasticSearch聚合 前言 说完了ES的索引与检索,接着再介绍一个ES高级功能API – 聚合(Aggregations),聚合功能为ES注入了统计分析的血统,使用户在面对大数据提 ...
- ElasticSearch聚合
前言 说完了ES的索引与检索,接着再介绍一个ES高级功能API – 聚合(Aggregations),聚合功能为ES注入了统计分析的血统,使用户在面对大数据提取统计指标时变得游刃有余.同样的工作,你在 ...
- Elasticsearch 聚合统计与SQL聚合统计语法对比(一)
Es相比关系型数据库在数据检索方面有着极大的优势,在处理亿级数据时,可谓是毫秒级响应,我们在使用Es时不仅仅进行简单的查询,有时候会做一些数据统计与分析,如果你以前是使用的关系型数据库,那么Es的数据 ...
- ElasticSearch聚合分析
聚合用于分析查询结果集的统计指标,我们以观看日志分析为例,介绍各种常用的ElasticSearch聚合操作. 目录: 查询用户观看视频数和观看时长 聚合分页器 查询视频uv 单个视频uv 批量查询视频 ...
- ElasticSearch 聚合分析
公号:码农充电站pro 主页:https://codeshellme.github.io ES 中的聚合分析(Aggregations)是对数据的统计分析功能,它的优点是实时性较高,相比于 Hadoo ...
- ElasticSearch 聚合函数
一.简单聚合 桶 :简单来说就是满足特定条件的文档的集合. 指标:大多数 指标 是简单的数学运算(例如最小值.平均值.最大值,还有汇总),这些是通过文档的值来计算. 桶能让我们划分文档到有意义的集合, ...
- Mysql 单表查询-排序-分页-group by初识
Mysql 单表查询-排序-分页-group by初识 对于select 来说, 分组聚合(((group by; aggregation), 排序 (order by** ), 分页查询 (limi ...
- [SQL] SQL 基础知识梳理(三) - 聚合和排序
SQL 基础知识梳理(三) - 聚合和排序 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5926689.html 序 这是<SQL 基础知识梳理 ...
- 计数排序和桶排序(Java实现)
目录 比较和非比较的区别 计数排序 计数排序适用数据范围 过程分析 桶排序 网络流传桶排序算法勘误 桶排序适用数据范围 过程分析 比较和非比较的区别 常见的快速排序.归并排序.堆排序.冒泡排序等属于比 ...
随机推荐
- NC15033 小G有一个大树
题目链接 题目 题目描述 小G想要把自己家院子里的橘子树搬到家门口(QAQ..就当小G是大力水手吧) 可是小G是个平衡性灰常灰常差的人,他想找到一个这个橘子树的平衡点. 怎么描述这棵树呢...就把它看 ...
- TS内置类型与拓展
TS内置类型与拓展 TypeScript具有类型系统,且是JavaScript的超集,其可以编译成普通的JavaScript代码,也就是说,其是带有类型检查的JavaScript. 内置类型 Type ...
- Git实战系列教程
介绍 本文详细记录了Git一系列核心概念和工作中常用的操作命令,通篇以实际出发拒绝过度理论,值得典藏:). 概念 版本管理系统 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的 ...
- 网络通信部分之bs/cs架构,网络概念,osi七层网络模型,TCP/UDP协议---day27
1.网络开发的两大架构c/s,b/s # ### 1.网络开发的两大架构 a文件 -> b文件 借助c文件 a文件和b文件进行数据交流,借助c文件中转数据 a文件把数据放在c文件中,b文件从c文 ...
- python中partial用法
应用 典型的,函数在执行时,要带上所有必要的参数进行调用.然后,有时参数可以在函数被调用之前提前获知.这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用. 示例pyqt5 ...
- ASP.NET Core 微信支付(一)【统一下单 APIV3】
官方参考资料 签名:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml 签名生成:https://wechatp ...
- 【应用服务 App Service】在Azure Web App的部署文件中,是否可以限制某些文件无法被访问?(如json)
问题描述 当部署文件到Azure App Service上后,默认访问文件在wwwroot目录中,如appsettings.json文件,在通过URL+文件名的形式可以访问,这样敏感信息会被泄露出去, ...
- window.open代理劫持
window.open = new Proxy(window.open, { apply(target, ctx, args) { if (hasAuth(args[0])) { return tar ...
- 如何获取拼多多推流码并使用OBS进行直播-疯狂URL
简介 拼多多直播在PC端可以用多多视频|多多直播端进行开播,它的功能类似于常见的抖音直播助手和快手直播伴侣等等客户端.此教程测试时间 2023-7-12,第三方随时可能会升级,无法保证时效,建议不要升 ...
- APISIX的安装和简单使用
APISIX 是一个云原生.高性能.可扩展的微服务 API 网关. 它是基于 Nginx 和 etcd 来实现,和传统 API 网关相比,APISIX 具备动态路由和插件热加载,特别适合微服务体系下的 ...