使用sql来替换elasticsearch的PipelineAggregatorBuilders.bucketSelector()

前言

22届java来公司,刚好遇到公司技术栈升级换代。二话不说上来就是把公司骨灰级代码搬出来,然后选其中一个子系统交给我让我改。别说摸着石头过河了,这边这个开发部刚成立,就一个老员工还不是很熟悉es,我直接自己开路!
期间,这老代码基本没注释,唯一看到的只有三行看到一些前辈在代码中气的吐血写下的一些骂人的注释,起初年少轻狂,不以为意,后来改到那个地方了才知道,前辈骂的对啊!就比如这个破PipelineAggregatorBuilders.bucketSelector(),要把它用sql替换了。里面定义的一些变量,真是惨无人道!

解决方案

在网上查了好久没有查到合适的解决方法。后来无意间看到网上说PipelineAggregatorBuilders.bucketSelector()相当于聚合后的having,然后恍然大悟,困扰我两周的问题算是有了头绪。再看,公司的业务比较复杂,这个方法所在的业务里es嵌套聚合了三层。其中用了它两层。然后,由于嵌套的厉害,业务需求也没有什么可参考的,唯一的就是从老代码中参悟玄机,这里我一度陷入了逻辑死角,总觉得改不出来。后来,另辟蹊径,尝试着理解业务场景,明白es写的老代码是要做什么,然后需要怎么改,就这样慢慢地推演出了符合业务场景的sql,把这个问题给解决了。

模拟业务场景

  • 假设业务需求是:商店里有台智能设备,它可以识别每个用户,记录每个用户每次在商店里购物的时间以及花费的金额。现在,商店的负责人需要找出商店的大金主(即经常大额消费的顾客),准备向这些大金主下发VIP购物卡,以此来增加这些大金主的粘性,避免流失。(即寻找出一周(7天))内消费1000元的顾客。

先看es的写法,没错,就是这么套娃。

List<AggregationBuilder> aggregationList = new ArrayList<>();
		TermsAggregationBuilder userBuilder = AggregationBuilders.terms(UserDao.NAME+"Agg").field(UserDao.NAME).size(Integer.MAX_VALUE);
		DateHistogramAggregationBuilder timeBuilder = AggregationBuilders.dateHistogram(UserDao.TIME+"Agg").field(UserDao.TIME).dateHistogramInterval(DateHistogramInterval.DAY).format("yyyyMMdd").offset("+8h");
		timeBuilder.subAggregation(AggregationBuilders.sum(UserDao.MONEY+"Agg").field(UserDao.MONEY));
timeBuilder.subAggregation(PipelineAggregatorBuilders.bucketSelector("user_money", new Script("params._value0 > 1000"), UserDao.MONEY+"Agg"));	userBuilder.subAggregation(PipelineAggregatorBuilders.bucketSelector("user_time", new Script("params._value0 >= 7"), UserDao.TIME+"Agg._bucket_count"));
		
		userBuilder.subAggregation(timeBuilder);
		aggregationList.add(userBuilder);

在改sql之前要搞清楚,es中桶聚合相当于是group by 不过它还是有些不太一样。它的那个bucket桶有一个类似于count的属性,可以方便地统计每个桶中有多少文档。而如果是sql,除了嵌套sql之外,我们不能够计算出每个字段group by分组之后的count。但是,注意下,这里的管道查询的桶选择器,由于它的存在,相当于把嵌套sql直接给干死。然后只能多字段分组查询。然而多字段分组查询,这样我们就不能像es那样,得到每个桶聚合后的count了,因此这里我一度陷入了坑里,久久爬不出来。后来,一遍一遍分析代码从查询的结果中拿到的是什么数据,而它又想干什么。慢慢弄清楚业务场景,然后才发觉,这个count在这里我是不需要的分组统计的,我最终需要的是user。分析清楚后,我把思绪整理整理,最终拼接sql如下:

select name as nameAgg,time as timeAgg,count(time) as timeCount sum(money) as moneyAgg  from usertable  where ...一些条件 group by name,time having timeCount >= 7 and moneyAgg > 1000;

总结

最后,我想说,当遇到问题时,不要抱怨,不要害怕。首先去网上搜,搜不到的去官网,es中好多复杂的查询以及业务场景,网上很少涉及。有些大部分都是简单入个门,真正复杂的需要我们去官网查,结合业务场景推理业务逻辑。搞清楚时,再下手去做。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

“小笨熊”

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值