Elasticsearch 评分排序
- 背景
- 通过脚本改变评分
背景
近期有一个需求,需要对优惠券可用商品列表加个排序,只针对面值类的券不包括折扣券。
需求是这样的,假设有一张面值券 50 块钱,可用商品列表 A 100、B 40、C 10,当用户查询当前券可用商品列表的时候优先将卡券可以直接抵扣且不需要用户在额外支付的商品排在前面。
C 10
B 40
A 100
其实排序有很多侧重,比如:
1.根据用户利益最大化原则,排序列表应该是 B、C、A
2.根据用户购买习惯,有可能是 A、B、C
3.根据运营策略、第三方利益等有可能是C、B、A
这里暂且先不扩展如何对商品列表进行智能排序,如果需要完整的个性化商品推荐,涉及很多东西,后面有经验在拿来分享。
我们就这个简单的 case,一开始最直接的想法就是加个排序列,建索引的时候将排序值计算好直接写入。后来分析了下原来索引(__index__) 结构不是这种笛卡尔积的排列,所以在短时间内很难立马上线,需要新建 index 结构。
后来通过讨论用影响评分的方法来解决,可以节省时间快速上线。
通过脚本改变评分
ES query DSL 支持很多种类型的查询,结果的排序如果没有特殊声明 sort field 则是根据es打分(__score__)来排序的,__score__ 分值越高排序越靠前。
ES score 计算比较复杂,涉及到 TF(词频)/IDF(逆向文档频率)__、__罕见词__、__匹配文档长度__、__权重 boost 向量空间模型 等,不过 ES 提供了几种封装好的评分插件供使用。
function_score 查询来让我们根据业务场景改变文档评分方法,根据业务场景我们需要完全控制 score 生成的逻辑,所以我们选择 script_score 方式。
script_score
如果需求超出以上范围时,用自定义脚本可以完全控制评分计算,实现所需逻辑。
(参考:https://www.elastic.co/guide/cn/elasticsearch/guide/current/function-score-query.html)
脚本默认是 __groovy__,当然也可以根据需要使用其他脚本语言,我们来看下实现。
script.inline: on script.enfine.groovy.inline.aggs: on script.indexed: on script.file: on
首先在 es.yml 配置中打开脚本支持相关选项。
{ "query": { "function_score": { "query": { "bool": { "should": [ { "match": { "productName": "英语" } } ] } }, "score_mode": "first", "script_score": { "lang": "groovy", "params": { "couponPrice": 100 }, "script": "def deduct = couponPrice - doc['unitCost'].value.toFloat(); if (deduct > 0) {return 10000 + deduct;}else if(deduct==0 || (deduct<1 && deduct>0)){return 20000;}else{return doc['unitCost'].value.toFloat()-couponPrice;}" }, "boost_mode": "replace" } }, "from": 0, "size": 100 }
查询条件可以任意,关键是 script_score 对象,__script__ 是需要 ES 脚本引擎执行的脚本代码。
一个比较重要的选项 boost_mode ,__boost_mode__ 是控制整个 document 的评分方式,这里我们选择替代(__replace__)默认计算好的评分。
这里面的排序有一个小技巧,如何将负数排序在前面,正数排序在后面,还有抵扣后是0的处理。
def deduct = couponPrice - doc['unitCost'].value.toFloat(); if (deduct > 0) { return 10000 + deduct; }else if(deduct==0 || (deduct<1 && deduct>0)){ return 20000; }else{ return doc['unitCost'].value.toFloat()-couponPrice; }
通过 couponPrice 变量表示优惠券面值金额,如果当前商品抵扣完是负数说明需要排序在前面,那么如何和抵扣完正数分开尼,这里可以取一个稍微大点的值加上抵扣后的负值,这样把负值转换成正数自然就排序在前面。
抵扣后等于0的或者小于1大于0的值也是可以优先安排在前面,当然这里还是不够灵活的,最好的方式是根据当前面值、商品价格动态计算才准确。
最后就是抵扣完需要用户在额外支付的排在最后面,直接取需要额外支付的金额数值作为排序。
通过 ES 评分我们能做很多事情,这个case只是一个简单的场景。
作者:王清培 (沪江集团资深架构师)
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Streaming with Apache Training
Apache Flink流式传输 本次培训主要专注在四个重要的概念:连续处理流数据,事件时间,有状态的流处理和状态快照。 流处理 流是数据天然的栖息地,无论是来自Web服务器的事件,来自证券交易所的交易,还是来自工厂车间的机器传感器读数,数据都是作为流的一部分创建的。但是当我们分析数据时,我们可以围绕有界或无界流组织我们的处理过程,我们选择的范式会产生生远的影响。 批处理是我们处理有界数据流时的工作范例。这种操作模式中我们可以选择在产生任何结果之前注入整个数据集,例如,对数据进行排序,计算全局统计信息或生成汇总所有输入的最终报告。 流处理另一方面,流处理涉及无界数据流。从概念上来说,至少输入可能永远不会结束,因此我们被迫在数据抵达时进行连续处理。 在Flink中,应用程序由用户定义的算子转换的数据流组成。这些数据流形成有向图,这些图以一个或多个源开头,并以一个或多个接收器结束。 一个应用可能从流式源消费实时数据如消息队列或分布式日志,例如Apache Kafka或Kinesis。但是Flink也可以从很多数据源中获取有界的,历史的数据。类似的,Flink应用程序生成的结果流可以发送到各...
- 下一篇
hadoop搭建之hadoop安装
运行环境:系统:centos6.9jdk:jdk1.8.0_201 搭建版本:hadoop:hadoop-2.7.2 环境说明:集群环境至少需要 3 个节点(也就是 3 台服务器设备):1 个 Master,2 个 Slave,节点之间局域网连接 1、首先需要分别将三台服务器的opt目录改为qiqi用户的 chown -R qiqi: /opt/ 2、在opt目录下创建文件夹 modules 和 文件夹software,所有的安装包放在software下面,解压文件放在modules下面 3、准备工作,安装 JDK,在三台机器上配置 JDK 环境 tar -zvxf /opt/software/jdk-8u201-linux-x64.tar.gz -C /opt/modules/ 4、配置环境变量(切换到root目录下):vi /etc/profile # set java export JAVA_HOME=/opt/madules/jdk1.8.0_201 export JRE_HOME=$JAVA_HOME/jre export CLASSPATH=.:$JAVA_HOME/lib...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Hadoop3单机部署,实现最简伪集群
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果