ES 自定义打分

Elasticsearch 会为 query 的每个文档计算一个相关度得分 score ,并默认按照 score 从高到低的顺序返回搜索结果。 在很多场景下,我们不仅需要搜索到匹配的结果,还需要能够按照某种方式对搜索结果重新打分排序。例如:

  • 搜索具有某个关键词的文档,同时考虑到文档的时效性进行综合排序。
  • 搜索某个旅游景点附近的酒店,同时根据距离远近和价格等因素综合排序。
  • 搜索标题包含 elasticsearch 的文章,同时根据浏览次数和点赞数进行综合排序。

Function score query 就可以让我们实现对最终 score 的自定义打分。

score 自定义打分过程

为了方便,下面把 ES 对 query 匹配的文档进行打分得到的 score 记为 query_score ,而最终搜索结果的 score 记为 result_score ,显然,一般情况下(也就是不使用自定义打分时),result_score 就是 query_score

那么当我们使用了自定义打分之后呢?最终结果的 score 即 result_score 的计算过程如下:

1.跟原来一样执行 query 并且得到原来的 query_score

2.执行设置的自定义打分函数,并为每个文档得到一个新的分数,本文记为 func_score

3.最终结果的分数 result_score 等于 query_scorefunc_score 按某种方式计算的结果(默认是相乘)。

例如,搜索标题包含 elasticsearch 的文档。

不使用自定义打分,则搜索形如:

GET /_search
{"query": {"match": {"title": "elasticsearch"}}
}

假设我们最终得到了三个搜索结果,score 分别是 0.3、0.2、0.1

使用自定义打分,即 function_score ,则语法形如:

GET /_search
{"query": {"function_score": {"query": {"match": {"title": "elasticsearch"}}<!-- 设置自定义打分函数,这里先省略,后面再展开讲解 -->"boost_mode": "multiply"}}
}

最终搜索结果 score 的计算过程就是:

1.执行 query 得到原始的分数,与上文假设对应,即 query_score 分别是 0.3、0.2、0.1

2.执行自定义的打分函数,这一步会为每个文档得到一个新的分数,假设新的分数即 func_score 分别是 1、3、5

3.最终结果的 score 分数即 result_score = query_score * func_score ,对应假设的三个搜索结果最终的 score 分别就是 0.3 * 1 = 0.30.2 * 3 = 0.60.1 * 5 = 0.5 ,至此我们完成了新的打分过程,而搜索结果也会按照最终的 score 降序排列。

最终的分数 result_score 是由 query_scorefunc_score 进行计算而来,计算方式由参数 boost_mode 定义:

multiply : 相乘(默认),result_score = query_score * function_score

replace : 替换,result_score = function_score

sum : 相加,result_score = query_score + function_score

avg : 取两者的平均值,result_score = Avg(query_score, function_score)

max : 取两者之中的最大值,result_score = Max(query_score, function_score)

min : 取两者之中的最小值,result_score = Min(query_score, function_score)

function_score 打分函数

function_score 提供了以下几种打分的函数:

weight : 加权。

random_score : 随机打分。

field_value_factor : 使用字段的数值参与计算分数。

decay_function : 衰减函数 gauss, linear, exp 等。

script_score : 自定义脚本。

weight

weight 加权,也就是给每个文档一个权重值。

示例:

{"query": {"function_score": {"query": { "match": { "message": "elasticsearch" } },"weight": 5}}
}

例子中的 weight 是 5 ,即自定义函数得分 func_score = 5 ,最终结果的 score 等于 query_score * 5 。

当然这个示例将匹配项全部加权并不会改变搜索结果顺序,我们再看一个例子:

{"query": {"function_score": {"query": { "match": { "message": "elasticsearch" } },"functions": [{"filter": { "match": { "title": "elasticsearch" } },"weight": 5}]}}
}

我们可以通过 filter 去限制 weight 的作用范围,另外我们可以在 functions 中同时使用多个打分函数。

random_score

random_score 随机打分,生成 [0, 1) 之间均匀分布的随机分数值。

示例:

GET /_search
{"query": {"function_score": {"random_score": {}}}
}

虽然是随机值,但是有时候我们需要随机值保持一致,比如所有用户都随机产生搜索结果,但是同一个用户的随机结果前后保持一致,这时只需要为同一个用户指定相同的 seed 即可。

示例:

{"query": {"function_score": {"random_score": {"seed": 10,"field": "_seq_no"}}}
}

默认情况下,即不设置 field 时会使用 Lucene doc ids 作为随机源去生成随机值,但是这会消耗大量内存,官方建议可以设置 field_seq_no ,主要注意的是,即使指定了相同的 seed ,随机值某些情况下也会改变,这是因为一旦字段进行了更新,_seq_no 也会更新,进而导致随机源发生变化。

多个函数组合示例:

GET /_search
{"query": {"function_score": {"query": { "match_all": {} },"boost": "5","functions": [{"filter": { "match": { "test": "bar" } },"random_score": {},"weight": 23},{"filter": { "match": { "test": "cat" } },"weight": 42}],"max_boost": 42,"score_mode": "max","boost_mode": "multiply","min_score": 42}}
}

上例 functions 中设置了两个打分函数:

  • 一个是 random_score 随机打分,并且 weight 是 23
  • 另一个只有 weight 是 42

假设:

•第一个函数随机打分得到了 0.1 ,再与 weight 相乘就是 2.3

•第二个函数只有 weight ,那么这个函数得到的分数就是 weight 的值 42

score_mode 设置为了 max,意思是取两个打分函数的最大值作为 func_score,对应上述假设也就是 2.3 和 42 两者中的最大值,即 func_score = 42

boost_mode 设置为了 multiply,就是把原来的 query_scorefunc_score 相乘就得到了最终的 score 分数。

参数 score_mode 指定多个打分函数如何组合计算出新的分数:

multiply : 分数相乘(默认)

sum : 相加

avg : 加权平均值

first : 使用第一个 filter 函数的分数

max : 取最大值

min : 取最小值

为了避免新的分数的数值过高,可以通过 max_boost 参数去设置上限。

需要注意的是:不论我们怎么自定义打分,都不会改变原始 query 的匹配行为,我们自定义打分,都是在原始 query 查询结束后,对每一个匹配的文档进行重新算分。

为了排除掉一些分数太低的结果,我们可以通过 min_score 参数设置最小分数阈值。

field_value_factor

field_value_factor 使用字段的数值参与计算分数。

例如使用 likes 点赞数字段进行综合搜索:

{"query": {"function_score": {"query": { "match": { "message": "elasticsearch" } },"field_value_factor": {"field": "likes","factor": 1.2,"missing": 1,"modifier": "log1p"}}}
}

说明:

field : 参与计算的字段。

factor : 乘积因子,默认为 1 ,将会与 field 的字段值相乘。

missing : 如果 field 字段不存在则使用 missing 指定的缺省值。

modifier : 计算函数,为了避免分数相差过大,用于平滑分数,可以是以下之一:

​ •none : 不处理,默认

​ •log : log(factor * field_value)

​ •log1p : log(1 + factor * field_value)

​ •log2p : log(2 + factor * field_value)

​ •ln : ln(factor * field_value)ln1p : ln(1 + factor * field_value)

​ •ln2p : ln(2 + factor * field_value)

​ •square : 平方,(factor * field_value)^2

​ •sqrt : 开方,sqrt(factor * field_value)

​ •reciprocal : 求倒数,1/(factor * field_value)

假设某个匹配的文档的点赞数是 1000 ,那么例子中其打分函数生成的分数就是 log(1 + 1.2 * 1000),最终的分数是原来的 query 分数与此打分函数分数相差的结果。

decay_function

decay_function 衰减函数,例如:

•以某个数值作为中心点,距离多少的范围之外逐渐衰减(缩小分数)•以某个日期作为中心点,距离多久的范围之外逐渐衰减(缩小分数)

•以某个地理位置点作为中心点,方圆多少距离之外逐渐衰减(缩小分数)

示例:

"DECAY_FUNCTION": {"FIELD_NAME": {"origin": "30, 120","scale": "2km","offset": "0km","decay": 0.33}
}

上例的意思就是在距中心点方圆 2 公里之外,分数减少到三分之一(乘以 decay 的值 0.33)。

DECAY_FUNCTION 可以是以下任意一种函数:

​ •linear : 线性函数

​ •exp : 指数函数

​ •gauss : 高斯函数

origin : 中心点,只能是数值、日期、geo-point

scale : 定义到中心点的距离

offset : 偏移量,默认 0

decay : 衰减指数,默认是 0.5

示例:

GET /_search
{"query": {"function_score": {"gauss": {"@timestamp": {"origin": "2013-09-17","scale": "10d","offset": "5d","decay": 0.5}}}}
}

中心点是 2013-09-17 日期,scale 是 10d 意味着日期范围是 2013-09-12 到 2013-09-22 的文档分数权重是 1 ,日期在 scale + offset = 15d 之外的文档权重是 0.5 。

如果参与计算的字段有多个值,默认选择最靠近中心点的值,也就是离中心点的最近距离,可以通过 multi_value_mode 设置:

min : 最近距离

max : 最远距离

avg : 平均距离

sum : 所有距离累加

示例:

GET /_search
{"query": {"function_score": {"query": {"match": {"properties": "大阳台"}},"functions": [{"gauss": {"price": {"origin": "0","scale": "2000"}}},{"gauss": {"location": {"origin": "30, 120","scale": "2km"}}}],"score_mode": "multiply"}}
}

假设这是搜索大阳台的房源,上例设置了 price 价格字段的中心点是 0 ,范围 2000 以内,以及 location 地理位置字段的中心点是 “30, 120” ,方圆 2km 之内,在这个范围之外的匹配结果的 score 分数会进行高斯衰减,即打分降低。

script_score

script_score 自定义脚本打分,可以直接编写脚本打分。

示例:

GET /_search
{"query": {"function_score": {"query": {"match": { "message": "elasticsearch" }},"script_score": {"script": {"source": "Math.log(2 + doc['my-int'].value)"}}}}
}

在脚本中通过 doc['field'] 的形式去引用字段,doc['field'].value 就是使用字段值。

可以把额外的参数与脚本内容分开:

GET /_search
{"query": {"function_score": {"query": {"match": { "message": "elasticsearch" }},"script_score": {"script": {"params": {"a": 5,"b": 1.2},"source": "params.a / Math.pow(params.b, doc['my-int'].value)"}}}}
}

本文原创作者:奇想派、一名努力分享的程序员。

文章首发平台:微信公众号【编程达人】

原创不易!各位小伙伴觉得文章不错的话,不妨关注公众号,进行点赞(在看)、转发三连走起!谢谢大家!

elasticsearch自定义打分操作相关推荐

  1. ElasticSearch painless脚本实现自定义打分排序

    背景: 遇到了这样一个需求,对于历史购买的商品优先排序 自定义打分排序规则 自定义打分规则,其实就是可以根据打分规则进行排序.注意:只能对索引的keyword属性进行排序,TEXT类型的要加.keyW ...

  2. 【Elasticsearch】Elasticsearch自定义评分的N种方法

    1.概述 首先参考文章:[Elasticsearch]Elasticsearch 相关度评分 TF&IDF 然后转载文章:实战 | Elasticsearch自定义评分的N种方法 2.三个问题 ...

  3. ElasticSearch自定义词库

    由于网络词语层出不穷,ik分词器有时并不能完全识别网络词汇,如下: 按照网络词语,王者荣耀应该被识别为一个词语,而不是被拆分成2个. 所以这时需要自定义词库来解决以上问题. 自定义词库 自定义扩展词库 ...

  4. Hadoop集群中增加与ElasticSearch连接的操作

    在没有引入elasticsearch-hadoop-xxx.jar相应的Jar包时,的在Hive中执行ElasticSearch外部表操作,会报如下的异常: Exception in thread & ...

  5. elasticsearch 自定义_id

    elasticsearch 自定义ID: curl -s -XPUT localhost:9200/web -d ' {"mappings": {"blog": ...

  6. ant4 多个form 验证_ant-design表单处理和常用方法及自定义验证操作

    首先要说一下antdesign这个框架API和demo丰富,而且开发环境提供对应的warning来纠正用户的错误.是一个很好的组件库. 关于表单验证方面是依赖于 async-validator 库.百 ...

  7. 【Elasticsearch】打分策略详解与explain手把手计算

    一.目的 一个搜索引擎使用的时候必定需要排序这个模块,一般情况下在不选择按照某一字段排序的情况下,都是按照打分的高低进行一个默认排序的,所以如果正式使用的话,必须对默认排序的打分策略有一个详细的了解才 ...

  8. 【Es】ElasticSearch 自定义分词器

    1.分词器 转载:https://blog.csdn.net/gwd1154978352/article/details/83343933 分词器首先看文章:[Elasticsearch]Elasti ...

  9. vue页面返回消息头获取_vue在响应头response中获取自定义headers操作

    日常开发,我们可能会为了安全问题,保证第三方无法通过伪造返回报文欺骗前端,需要在返回报文中添加自定义参数,用于验证身份,后端添加自定义参数,前端校验自定义参数通过后才会执行相应的操作. 系统为了安全会 ...

最新文章

  1. 局域网交换(交换机三大原理.基本配置)
  2. ACID、数据库隔离级别
  3. python 内置模块 增加_模块的内置方法--Python提高班
  4. The jar file has no source attachment
  5. 这怕是我看过的最好的关于 “ 拜占庭将军问题 ” 的文章
  6. python windows 安装scrapy_Windows下安装Scrapy
  7. linux db2创建存储过程语法,EF基础一-db2存储过程中循环语句while do...-oracle 创建DBLINK_169IT.COM...
  8. 东北大哥在线反套路hhhhhh | 今日最佳
  9. 用OpenStack构建南方电网广东公司能源云
  10. 厉害了网页扫码,所有方法都给你总结到这了,赶紧收藏
  11. 玩转Kinetis之教你将K60主频超到200MHz以上
  12. 如何用计算机解一元三次方程,利用Excel电子表格解一元三次方程
  13. Windows许可证过期(‘slmgr.vbs‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件)
  14. 串口转以太网服务器市场现状研究分析与发展前景预测报告
  15. 戴尔7040linux改装win7,戴尔OptiPlex 3060台式机win10改win7系统(完美支持usb)
  16. 怎么批量删除群共享里的文件
  17. CnPeng杂说:油条的来历
  18. 计算机教学反思杂文,懒教学反思随笔
  19. 职业教育/培训研究报告(精选七篇)
  20. 2022 IDC报告出炉 思腾合力一举拿下全国服务器市场双项榜单

热门文章

  1. 【Go语言入门100题】038 新世界 (5 分) Go语言|Golang
  2. NBA常规赛总抢断排行榜(数据截止至11年4月14日)
  3. 【原创】MacOS 安装 homebrew-cask
  4. 廖雪峰python教程学习之习题解析
  5. 仿京东、饿了么 左右联动菜单列表自定义View
  6. 独家 | PHM数据竞赛首个中国夺冠团队经验分享(常用模型赛题详解PPT视频)...
  7. 微信6.5.7手机号码如何解绑
  8. 有赞android电话面试,有赞校招面试总结
  9. 9岁有赞:新零售业务快速增长 推新品牌扶持计划
  10. 分鱼问题c语言,五人分鱼问题(附答案)