Elasticsearch 评分排序

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 背景通过脚本改变评分背景近期有一个需求,需要对优惠券可用商品列表加个排序,只针对面值类的券不包括折扣券。需求是这样的,假设有一张面值券 50 块钱,可用商品列表 A 100、B 40、C 10,当用户查询当前券可用商品列表的时候优先将卡券可以直接抵扣且不需要用户在额外支付的商品排在前面。

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

背景

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

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

作者:王清培 (沪江集团资深架构师)
相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
JSON Ubuntu Java
Elasticsearch聚合学习之四:结果排序
在前面的实战中,聚合的结果以桶(bucket)为单位,放在JSON数组中返回,这些数据是没有排序的,今天来学习如何给这些数据进行排序
459 0
Elasticsearch聚合学习之四:结果排序
|
4月前
|
SQL JSON 大数据
ElasticSearch的简单介绍与使用【进阶检索】 实时搜索 | 分布式搜索 | 全文搜索 | 大数据处理 | 搜索过滤 | 搜索排序
这篇文章是Elasticsearch的进阶使用指南,涵盖了Search API的两种检索方式、Query DSL的基本语法和多种查询示例,包括全文检索、短语匹配、多字段匹配、复合查询、结果过滤、聚合操作以及Mapping的概念和操作,还讨论了Elasticsearch 7.x和8.x版本中type概念的变更和数据迁移的方法。
ElasticSearch的简单介绍与使用【进阶检索】 实时搜索 | 分布式搜索 | 全文搜索 | 大数据处理 | 搜索过滤 | 搜索排序
|
7月前
|
机器学习/深度学习 数据挖掘 索引
Elasticsearch 如何把评分限定在0到1之间?
Elasticsearch 如何把评分限定在0到1之间?
174 0
|
7月前
|
测试技术 定位技术 API
万字长文:一文彻底搞懂Elasticsearch中Geo数据类型查询、聚合、排序
万字长文:一文彻底搞懂Elasticsearch中Geo数据类型查询、聚合、排序
95181 140
|
5月前
|
数据库
面试题ES问题之Elasticsearch的排序分页和高亮功能如何解决
面试题ES问题之Elasticsearch的排序分页和高亮功能如何解决
44 0
|
7月前
|
搜索推荐 JavaScript Java
Elasticsearch 8.X 如何依据 Nested 嵌套类型的某个字段进行排序?
Elasticsearch 8.X 如何依据 Nested 嵌套类型的某个字段进行排序?
103 0
|
7月前
|
存储 数据建模 索引
来自钉钉群的问题——Elasticsearch 如何实现文件名自定义排序?
来自钉钉群的问题——Elasticsearch 如何实现文件名自定义排序?
61 0
|
存储 算法 API
Elasticsearch评分相关度算法解析
Elasticsearch评分相关度算法解析
152 0
|
7月前
|
存储 自然语言处理 Java
SpringBoot集成ElasticSearch时分页排序查询时遇到的坑每次只能返回10条数据
SpringBoot集成ElasticSearch时分页排序查询时遇到的坑每次只能返回10条数据
250 0
|
算法 索引
Elasticsearch - 聚合获取原始数据并分页&排序&模糊查询
Elasticsearch - 聚合获取原始数据并分页&排序&模糊查询
351 0
Elasticsearch - 聚合获取原始数据并分页&排序&模糊查询