很多时候业务上需要分组排序分页的场景,类似于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. 从零开始的微信小程序入门教程(二),初识WXML与WXSS

    壹 ❀ 引 时隔大半年,我终于开始写小程序入门教程的第二篇了,其实我也在纳闷,这么久的时间我到底干了什么,仔细一想,我学了JavaScript部分进阶知识,学了ES6,系统性的去复习了angularj ...

  2. QT & C++笔记

    语法 变量声明 直接声明的变量, 其赋值操作会产生值拷贝, 例如 QString b("some text"); QString a(b); int a = 10; int b = ...

  3. idea自定义代码片段live template

    1.介绍 有时在idea编辑器经常会写同一个代码块,那么这个代码块就可以利用live template功能把它定义成可根据关键字触发的代码片段,效果如下图: 2.操作步骤 此处我们就以springbo ...

  4. VMware虚拟机Ubuntu系统连接网络过程

    网络和Internet设置--高级网络设置--更多网络适配器选项--WLAN. 右键选择属性--共享,勾选允许连接,选择VMnet8.(若勾选了其它,之后再想换回来,可以先取消勾选,点确定,再进入勾选 ...

  5. m1芯片mac安装homebrew

    安装 ARM 版 Homebrew ARM版Homebrew最终被安装在/opt/homebrew路径下. 直接执行: /bin/bash -c "$(curl -fsSL https:// ...

  6. Java 常用类 String的常用方法(2)

    1 /** 2 * String 常用方法(2) 3 * boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束 4 * boolean startsWith ...

  7. 【Flink入门修炼】2-2 Flink State 状态

    什么是状态?状态有什么作用? 如果你来设计,对于一个流式服务,如何根据不断输入的数据计算呢? 又如何做故障恢复呢? 一.为什么要管理状态 流计算不像批计算,数据是持续流入的,而不是一个确定的数据集.在 ...

  8. PHP四则运算类(支持加、减、乘、除、小中括号)

    <?php /** * 四则运算(支持加.减.乘.除.小中括号) * Class calculator */ class calculator { //保留几位小数点 public $point ...

  9. C++ Qt开发:QFileSystemModel文件管理组件

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QFi ...

  10. 完整塔建一个spring 注解版 mybaties 过程可供复制代码

    第一步引导包.新建工程maven模块 pom.xml 中导入相对应包 ++++++++++++++++++++++++++++++++++++++++++++       1      +++++++ ...