ElasticSearch 评分排序

摘要:
ESqueryDSL通过脚本更改分数来支持多种类型的查询。如果没有关于sortfield的特殊语句,则根据es分数对结果进行排序。分数越高,分数越高。默认脚本是groovy。当然,您可以根据需要使用其他脚本语言。让我们看看实现。def扣除=couponPrice文档['unitCost'].value。toFloat();如果{return10000+扣除;}elseif{return20000;}否则{returndoc['unitCost'].value.toFloat()-couponPrice;}优惠券面值金额由couponPrice变量表示。如果当前商品扣除后为负数,则表示需要先进行排序。那么,扣除后的正数如何划分?在这里,您可以取一个稍大的值,并在扣除后添加负值,这样当负值转换为正值时,将首先对其进行排序。
  • 背景
  • 通过脚本改变评分
背景

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

需求是这样的,假设有一张面值券 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 评分排序》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇熊逸《唐诗50讲》快意篇pdf.js兼容ie9浏览器及以上版本下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ExtJS 4.2 教程-01:Hello ExtJS

关于ExtJS 是什么我就不多说了,本系列教程将介绍ExtJS 4.2的用法,这是本系列的第一篇,惯例从Hello World开始。 下载 ExtJS ExtJS 目前的最新版本是 4.2,我们可以从官方网站下载最新版本的ExtJS。 下载地址:http://cdn.sencha.com/ext/gpl/ext-4.2.1-gpl.zip 官网地址:ht...

jquery.validate.js的Validate表单验证

原文连接 https://blog.csdn.net/weixin_42765270/article/details/84591593 validate 一、 validate的使用步骤 引入jquery.min.js 引入 jquery.validate.js 页面加载后对表单进行验证 $("#表单id名").validate({}) 在valid...

ElasticSearch(四):关于es的一些基础知识讲解

上一篇博客更新完之后,我发现一个问题:在我创建索引的时候依旧无法准确的理解每个字段的意义,所以就有了这个。 1. 关于索引 1.1 关于索引的一些基础知识 在创建标准化索引的时候,我们传入的请求体如下: { "settings":{ "number_of_shards":5, "number_of_repli...

prometheus监控elasticsearch

prometheus监控es,同样采用exporter的方案。 项目地址: elasticsearch_exporter:https://github.com/justwatchcom/elasticsearch_exporter 1、安装部署 现有es三节点的集群,环境大概如下: 主机 组件 192.168.75.11 prometheus...

Elasticsearch: Join数据类型

在Elasticsearch中,Join可以让我们创建parent/child关系。Elasticsearch不是一个RDMS。通常join数据类型尽量不要使用,除非不得已。那么Elasticsearch为什么需要Join数据类型呢? 在Elasticsearch中,更新一个object需要root object一个完整的reindex: 即使是一个fi...

Java API操作ES

Java API操作ES Elastic Search软件是由Java语言开发的,所以也可以通过Java API的方法对Elastic Search服务进行访问。 1. 引入POM文件 <properties> <elasticsearch.version>7.6.2</elasticsearch.ve...