Skip to content

Function Score Query - Elastic Search

Posted on:2020-09-10 at 15:40

什么是 function score

function_score查询用于控制查询文档的评分,可以把它看做一个套子,套在你的任何查询之上,专门用来修改结果的分值。

这个查询常见的使用场景有两个,一个是简单的 modify 每个文档的评分,另一个是为了方便自定义排序,比如用来实现一个归一化评分的结果排序。 在第二种场景下,直接使用sort排序会过于绝对,因为它会忽略文档本身的相关度。比如按日期排序,得到的结果是绝对按日期排序的,query 本身的评分会被忽略。而function_score可以通过控制文档评分,间接达到排序的目的,使用起来更加灵活。

预定义的算分方法

一个简单的function_score的 DSL 格式如下:

{
"query": {
"function_score": {
"query": { "match_all": {} },
"weight": 2
}
}
}

function_score包裹了一个query,这里填写一个普通的查询,后面的weight是一个预定义的评分方法,用来给这个查询的评分添加权重。

预定义的算分方法有以下几种:

weight

weight 使用起来最简单,只需要设置一个数字作为权重,文档的分数就会乘以该权重。 和 boost 不同,它的结果不会被规范化。例如,当 weight 为 2 时,最终评分为 2 * _score

它最大的用途可能就是配合 filter 一起使用了,因为 filter 只会过滤出符合条件的文档,而不会计算文档的得分,所以只要满足过滤条件的文档得分都是 1,而 weight 可以把分数修改为你想要的值。

field_value_factor

用文档中的某个字段去计算分值。 比如搜索一类商品,你想在相似度排序的基础上,尽量按照商品的销售量降序,按照商品的价格升序,就可以使用这个 function。

它有以下几个属性:

比如上面说的商品搜索,让销售量sales较大的商品尽量靠前,那么 DSL 可以这样设计

{
"query": {
"function_score": {
"query": {
"match": {
"title": "外套"
}
},
"field_value_factor": {
"field": "sales",
"modifier": "log1p",
"factor": 0.1,
"missing": 1
},
"boost_mode": "sum"
}
}
}

这个查询的计算公式为

_score = _score + log(1 + 0.1 * sales)

random_score

产生一个 [0, 1) 的随机评分,它并不是完全随机的,而是根据种子seed、字段field、索引名、分片名来生成的随机数,所以当这四个值相同时,得到的分其实是不变的。

它具有以下属性:

利用这个随机评分的特性,可以实现个性化推荐之类的场景,它可以让不同用户请求得到不同结果,而同一用户请求得到相同的结果,是不是非常的 amazing 呀(毕导脸)。

例如查询一篇文章,并用 userId 产生随机评分,DSL 可以这样设计

{
"query": {
"function_score": {
"query": {
"match": {
"title": "喜欢"
}
},
"random_score": {
"seed": 1,
"field": "userId"
},
"boost_mode": "replace"
}
}
}

衰减函数

衰减函数decay function提供了一个更复杂的公式,它可以让评分在多个标准之间权衡。

举个简单的例子,比如旅客搜索附近的酒店,这时候距离和价格都将影响用户的选择,有时候用户愿意为了价格便宜而选择一家比较远的酒店,而相反也可能为了住得近些付更多的钱。 这时候衰减函数可以在距离和价格上进行权衡,距离和价格就是这次查询的两个滑动标准。衰减函数可以很好的应用于数值、日期、地理位置类型的标准。

衰减函数具有以下几个属性:

衰减函数具有以下几种计算公式:

下图非常直观的展示了属性和公式对衰减的影响:

衰减曲线

在我们的例子里,衰减函数可以这样设置

上面的 DSL 可以这样设计:

{
"query": {
"function_score": {
"query": {
"match": {
"title": "酒店"
}
},
"gauss": {
"price": {
"origin": "120",
"offset": "10",
"scale": "30"
}
},
"boost_mode": "sum"
}
}
}

script_score

function_score提供的以上几种 function 已经可以解决大部分问题了,但可以看出它们还有一定的局限性:

如果上面的 function 都无法满足你的场景,可以使用script_score自行实现评分逻辑。它支持运行一段脚本来计算文档的分值,使用的语法是painless

一个简单的用例如下,使用文章的点赞数votes作为它的评分

{
"query": {
"function_score": {
"query": {
"match": {
"title": "文章"
}
},
"script_score": {
"script": {
"source": "doc['votes'].value"
}
},
"boost_mode": "replace"
}
}
}

script_score支持给脚本传入参数,例如将点赞数的 2 倍作为文档评分:

{
"script_score": {
"script": {
"params": {
"a": 2
},
"source": "params.a * doc['votes'].value"
}
}
}

组合多个函数

上面的例子都只使用了一个函数,如果需要多个函数组合计算评分,可以使用functions属性。

一个简单的示例:

{
"query": {
"function_score": {
"query": { "match_all": {} },
"boost": "5",
"functions": [
{
"filter": { "match": { "title": "猫咪" } },
"random_score": {},
"weight": 23
},
{
"filter": { "match": { "title": "熊猫" } },
"weight": 42
}
],
"max_boost": 42,
"score_mode": "max",
"boost_mode": "multiply",
"min_score": 42
}
}
}

functions是一个数组,其中每一个 function 都有以下属性

组合多个评分 function 时,可以通过score_mode属性控制组合方式:

score_mode

评分模式,表示如何合并 functions 的结果,有以下几个选项:

其他属性

除了以上介绍的属性,function_score还支持一些对查询结果后处理的属性

boost

应用于整个查询的权重提升

boost_mode

表示如何将函数结果与文档评分_score 进行合并,有以下几个选项:

max_boost

用于限制一个函数的最大效果,例如max_boost设为 20,那么无论每个函数的计算结果如何,最终的函数结果都不会大于 20。

min_score

最终文档得分 _score 小于min_score的文档将被过滤掉。