• 背景
  • 通过脚本改变评分

背景

近期有一个需求,需要对优惠券可用商品列表加个排序,只针对面值类的券不包括折扣券。

需求是这样的,假设有一张面值券 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_modeboost_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只是一个简单的场景。

作者:王清培 (沪江集团资深架构师)

ElasticSearch 评分排序的更多相关文章

  1. 用python+selenium抓取豆瓣读书中最受关注图书并按评分排序

    抓取豆瓣读书中的(http://book.douban.com/)最受关注图书,按照评分排序,并保存至txt文件中,需要抓取书籍的名称,作者,评分,体裁和一句话评 方法一: #coding=utf-8 ...

  2. 用python+selenium抓取豆瓣电影中的正在热映前12部电影并按评分排序

    抓取豆瓣电影(http://movie.douban.com/nowplaying/chengdu/)中的正在热映前12部电影,并按照评分排序,保存至txt文件 #coding=utf-8 from ...

  3. Aimp3的播放列表 按评分排序 落雨

    如图,添加评分选项,并保存,就可以在下图的选项里找到此选项,并按评分排序 效果图如下:还可以倒置,迅速使评分高的音乐排在播放列表的前面位置!! 转自百度知道: http://zhidao.baidu. ...

  4. ElasticSearch评分分析 explian 解释和一些查询理解

    ElasticSearch评分分析 explian 解释和一些查询理解 按照es-ik分析器安装了ik分词器.创建索引:PUT /index_ik_test.索引包含2个字段:content和nick ...

  5. ElasticSearch(7)-排序

    引用自ElaticSearch权威指南 一.排序 相关性排序 默认情况下,结果集会按照相关性进行排序 -- 相关性越高,排名越靠前. 这一章我们会讲述相关性是什么以及它是如何计算的. 在此之前,我们先 ...

  6. Elasticsearch 评分score计算中的Boost 和 queryNorm

    本来没有这篇文章,在公司分享ES的时候遇到一个问题,使用boost的时候,怎么从评分score中知道boost的影响. 虽然我们从查询结果可以直观看到,boost起了应有的作用,但是在explain的 ...

  7. ElasticSearch 自定义排序处理

    使用function_score进行分组处理,利用分组函数script_score进行自定义分值处理, 注意:使用script功能需要在配置中打开脚本功能: script.inline: on   s ...

  8. ElasticSearch 排序

    1.相关性排序 ElasticSearch为了按照相关性来排序,需要将相关性表示为一个数值,在 Elasticsearch 中, 相关性得分 由一个浮点数进行表示,并在搜索结果中通过 _score 参 ...

  9. Elasticsearch 分页坑之---评分一致导致数错乱

    面试:你懂什么是分布式系统吗?Redis分布式锁都不会?>>>   1.背景介绍 最近搞es搜索,match查询默认按照评分排序,发现有一部分数据评分一致,一开始也没注意,客户端调用 ...

随机推荐

  1. 获取Form表单数据转化成JSON对象

    $.fn.serializeObject = function() { var o = {}; var a = this.serializeArray(); $.each(a, function() ...

  2. 舞蹈链 DLX

    欢迎访问——该文出处-博客园-zhouzhendong 去博客园看该文章--传送门 舞蹈链是一个非常玄学的东西…… 问题模型 精确覆盖问题:在一个01矩阵中,是否可以选出一些行的集合,使得在这些行的集 ...

  3. 05. Matplotlib 1 |图表基本元素| 样式参数| 刻度 注释| 子图

    1.Matplotlib简介及图表窗口 Matplotlib → 一个python版的matlab绘图接口,以2D为主,支持python.numpy.pandas基本数据结构,运营高效且有较丰富的图表 ...

  4. 第一章:python基础语法| 字符编码| 条件语句...

    1.编程语言介绍 编程就是写代码,让计算机帮你做事情.计算机底层是电路,只认识二进制0和1.机器语言&汇编语言语言进化历史:机器.汇编.高级.机器语言只接受二进制代码:汇编语言是采用英文缩写的 ...

  5. oracle授权表权限给用户

    命令:grant  xxx权限 on Table to USERgrant select,insert,update,delete,all on 表名 to 用户名例如:将test表的查询权限赋予to ...

  6. Manjaro (KDE)安装踩坑记录

    1.如果双显卡无法安装系统可以进如BIOS屏蔽显卡后进入安装 2.如果安装kde版本后容易冻屏.死机,可以尝试安装闭源驱动 3.如果出现resolving time out 10000ms 这样的问题 ...

  7. docker 启动MySQL

    Docker启动mysql的坑2   正确启动mysql: docker run -p 3306:3306 --name mysql02 -e MYSQL_ROOT_PASSWORD=123456 - ...

  8. JavaScript 作用域的误区

    了解JavaScript的同学可能知道,JavaScript语言由于设计原因,导致语言本身存在很多先天性的不足,当然这并非设计者有意的,js语言最初是被设计来作为网页交互的脚本语言,依照现有的js语法 ...

  9. BZOJ.3227.[SDOI2008]红黑树tree(树形DP 思路)

    BZOJ orz MilkyWay天天做sxt! 首先可以树形DP:\(f[i][j][0/1]\)表示\(i\)个点的子树中,黑高度为\(j\),根节点为红/黑节点的最小红节点数(最大同理). 转移 ...

  10. 屏幕录制软件camtasia studio 8序列号激活

    注册名:TEAM MESMERiZE序列号:3MHCA-5DMCV-H89T8-V8GML-W6FB8 打开hosts文件:C:\Windows\System32\drivers\etc\hosts把 ...