Elasticsearch function_score
查询是可以自定义评分函数的方式进行打分,它允许为每个与主查询匹配的文档应用一个函数,以达到改变甚至完全替换原始查询评分 _score
的目的。
es 搜索结果默认会以文档的相关度进行排序,如果想要改变默认的排序规则,也可以通过 sort
参数指定一个或多个排序字段。但是使用 sort 排序过于绝对,适用规则化的层次排序,且它会直接忽略掉文档本身的相关度(根本不会去计算)。在很多时候这样做的效果并不好,这时候我们就需要对多个字段进行综合评估,具备一定泛化能力的评分排序,这时 function_score 就派上大用场了。
function_score 语句
示例
先上一个 function_score 语句示例:
{
"from": 0,
"size": 100,
"query": {
"function_score" : {
"query" : {
"bool" : {
"must" : [
{
"multi_match" : {
"query" : "三亚旅游",
"fields" : [
"title^1.0",
"content^1.0"
],
"type" : "best_fields",
"operator" : "OR",
"slop" : 0,
"prefix_length" : 0,
"max_expansions" : 50,
"minimum_should_match" : "100%",
"zero_terms_query" : "NONE",
"auto_generate_synonyms_phrase_query" : true,
"fuzzy_transpositions" : true,
"boost" : 1.0
}
},
],
"filter" : [
{
"term" : {
"status" : {
"value" : 1,
"boost" : 1.0
}
}
}
],
"adjust_pure_negative" : true,
"boost" : 1.0
}
},
"functions" : [
{
"filter" : {
"match_all" : {
"boost" : 1.0
}
},
"script_score" : {
"script" : {
"source" : "double total = 0;total += 0.2764982*doc['ctr_7d'].value;total += 0.1296483*_score;total += -0.0796372*Math.log1p(doc['like_cnt_4_display'].value);total += 0.0296372*doc['like_rate'].value;total = total-3.10574001;return 1.0/(1.0+Math.exp(-total));",
"lang" : "painless",
"params" : {
}
}
}
}
],
"boost_mode" : "multiply",
"max_boost" : 3.4028235E38,
"boost" : 1.0
}
},
"_source": {
"includes": ["id", "title", "content"],
"excludes": []
}
}
从上例中可以看出 function_score 语句主要分为两大块,一个是 query 子句,它负责匹配,另一个是 functions 子句,它起到对原有评分进行加强功能计算排序,其实就是重打分操作。
function_score 查询模板
function_score 查询模板可以分为两类,分别为单个加强函数的查询和多个加强函数的查询。
单个加强函数的查询模板:
{
"query": {
"function_score": {
"query": {.....}, // 主查询,查询完后会有一个 _score 评分
"field_value_factor": {...}, // 在 _score 的基础上进行强化评分
"boost_mode": "multiply", // 指定用哪种方式结合 _score 和 强化 score
"max_boost": 1.5 // 限制强化 score 的最高分,但是不会限制 _score
}
}
}
多个加强函数的查询模板:
{
"query": {
"function_score": {
"query": {.....},
"functions": [ // 可以有多个加强函数(或是 filter+加强函数),每一个加强函数会产生一个加强 score
{ "field_value_factor": ... },
{ "gauss": ... },
{ "filter": {...}, "weight": ... }
],
"score_mode": "sum", // 决定加强 score 们如何整合
"boost_mode": "multiply" // 决定最后的 functions 中 score 和 query score 的结合方式
}
}
}
function_score 参数
强化 _score 计算的函数
function_score 提供了几种内置加强 _score 计算的函数功能:
-
weight
:设置一个简单而不被规范化的权重提升值。weight 加强函数和 boost 参数比较类似,可以用于任何查询,不过有一点差别是 weight 不会被 Lucene 规范化(normalize)成难以理解的浮点数,而是直接被应用。
例如,当 weight 为 2 时,最终得分为
new_score = 2 * _score
。 -
field_value_factor
:指定文档中某个字段的值结合 _score 改变分数;{ "query": { "function_score": { "query": {.....}, "field_value_factor": { "field": "view_cnt", "modifier": "none", "factor": 1.2 }, "boost_mode": "multiply", "max_boost": 1.5 } } }
调整后的 function 分数公式为,
factor * doc['view_cnt'].value
;此外,针对指定字段的缺失情况,提供了缺省值的设置,以及数据计算方式,如下:
{ "query": { "function_score": { "query": {.....}, "field_value_factor": { "field": "view_cnt", "modifier": "ln1p", "missing": 1.0, "factor": 1.2 }, "boost_mode": "multiply", "max_boost": 1.5 } } }
function 分数为,
ln1p(1.2 * doc['view_cnt'].value)
,如果指定字段缺失用 missing 对应的值,至于和匹配的相关性分数 _score 如何结合需要下面的 boost_mode 参数来决定。 -
random_score
:使用一致性随机分值计算来对每个用户采用不同的结果排序方式,对相同用户仍然使用相同的排序方式,其本质上用的是 seed 种子参数,用户相关的 id 与 seed 构造映射关系,就可千人千面的效果,seed 不同排序结果也不同。具体示例如下:{ "query": { "function_score": { "query": {.....}, "random_score": { "seed": 10, "field": "_seq_no" } } } }
-
衰减函数(decay function):es 内置了三种衰减函数,分别是
linear
、exp
和gauss
;三种衰减函数的差别只在于衰减曲线的形状,在 DSL 的语法上的用法完全一样;linear 是线性函数,它是一条直线,exp 是指数函数,它先剧烈衰减然后变缓,最后一个是最常用的 guass,高斯函数是钟形的,它的衰减速率是先缓慢,然后变快,最后又放缓。实际使用中,衰减函数以某个字段的值为基准,距离某个值越近得分越高。
-
script_score
:当需求超出以上范围时,可以用自定义脚本完全控制评分计算。
其它辅助函数
boost_mode
参数决定 query 中的相关性分数和加强的函数分数的结合方式。
multiply
:默认的配置,两者分数相乘,new_score = _score * boost_score;sum
:两者相加,new_score = _score + boost_score;min
:取两者最小值,new_score = min(_score, boost_score);max
:取两者最大值,new_score = max(_score, boost_score);replace
:用 boost_score 替换 _score 值。
score_mode
参数决定 functions 里面的强化 score 如何结合,function_score 先会执行 score_mode 的设置,即先整合所有的强化计算,再执行 boost_mode 的配置,就是将 query 相关性分数和整合强化分数的结合。
multiply
:默认的配置,多个强化分数相乘;sum
:多个强化分数相加;min
:取多个强化分数最小值;max
:取多个强化分数最大值;avg
:取多个强化分数平均值;first
:使用首个函数的结果作为最终结果。
max_boost
:限制加强函数的最大效果,就是限制加强 score 最大能多少,但要注意不会限制 old_score。
- 如果加强 score 超过了 max_boost 限制的值,会把加强 score 的值设成 max_boost 的值;
- 假设加强 score 是5,而 max_boost 是2,因为加强 score 超出了 max_boost 的限制,所以 max_boost 就会把加强 score 改为2。简单的说,就是 final_score = min(整合后的 score, max_boost)。