ElasticSearch使用painless脚本小记
- 前几天项目中碰到了一个麻烦的问题,统计Doc中一个List中符合筛选条件的项的值的和(简单说就是Doc是商店为主体,统计该商店的某个时间段下单记录),开始用的是二次查询,但是后来数据量大了以后,第二次查询用的IDs查询传了几千个值,超过了ES的限制,最后只好拿出脚本进行解决。
- 先说一下Doc的结构,不相干的字段就删掉了
1 | { |
- 业务的查询需求是筛选2016-2018年,下单金额累计超过13000的商店(当然还有其他附加条件的查询),orderList还是Nested字段,最开始是进行二次查询,也就是首先将其他条件查询出来,然后对结果进行过滤,在这个日期下的符合条件的取出来ID,进行二次查询(这个条件改成Ids查询),最开始还能正常工作,后来不加上这个条件一下查出来一千条数据的时候就麻烦了,es默认Bool查询条件不能超过1024,硬着头皮改了ES配置,调成了1W,总算是正常工作了,但是不是长久之计,随着订单越来越多,迟早还会有问题,而且查询的时间越来越长了(查出来7000多条数据的时候已经超过10s了。。),跟业务部门沟通了,他们暂时可以接受,但是让我们尽快优化。
- 网上查找了很多资料,也想使用innerHits解决,但是innerHits结果无法参与aggs,实在没有其他办法,只好决定用脚本来解决。在上家公司用的是ES2.X,脚本groovy默认是关闭的,开启还有重启ES修改配置,觉得麻烦,而且据说有安全隐患,对这个一比较抵触,当下用的是ES5.6,看了一下脚本用的是painless,这个从来没有接触过,看了一下语法,好像也不太复杂,和Java差不太多,那就硬着头皮上。
- painless什么时候开始支持的没太关注,不过看文档说好像已经是容器内运行,相对比较安全了,而且ES5.6默认已经开启了,尝试过程中发现Nested类型取值只能取到一个内部文档的,list其他对象找不到。冗余出来一个非nested字段,发现日期和金额都是分别排序的,这就很尴尬了。。。最后决定吧订单金额和下单时间拼接成字符串,存到一个数组中去,这样取值的时候,金额和日期就是一一对应的。
- 修改后的Doc结构如下:
1 | { |
- 然后就是对照着文档写painless脚本了,个人水平有限,写的脚本也很低效,不过好赖问题是解决了,附上脚本:
1 | String fotmat = 'yyyy-MM-dd'; |
- 使用脚本解决了时间段下单次数统计和时间段金额统计的查询,时间也从原来的接近10s下降到1s左右(感觉脚本没办法走索引,应该都是全表扫描),这个肯定不是最优方法,但是 现在也想不出来什么其他高效的方法,就先上这个了,最起码目前查询最近5年的订单也是1秒左右,而且不用担心出现bool数量超过限制的问题了。