Function Score Query
function_score允许你修改通过查询获取文档的分数,很有用处,score function是计算昂贵的,以及在过滤一系列文档上计算分数是高效的
想要使用function_score,使用者必须要定义一个查询和一个或者多个方法,方法是用来为每个文档通过查询计算分数。
function_score 可以通过仅仅一个方法如下:
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d' { "query": { "function_score": { "query": { "match_all": {} }, "boost": "5", "random_score": {}, "boost_mode":"multiply" } } } '
此外,多种方法可以进行组合使用,既然这样其中一个可以随意选择执行方法只要一个文档匹配给定字段的查询
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d' { "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 } } } '
注意:过滤查询产生的分数不影响
如果没有过滤方法,这就相当于指定"match_all": {}
首先,每个文档通过定义的方法进行打分,参数"score_mode"指定了计算的分数是怎么组成的
- multiply 分数相乘(默认)
- sum 分数相加
- avg 分数平均
- first 第一个方法匹配过滤被应用
- max 最大的分数
- min 最小的分数
因为分数可以在不同的范围(例如:在0和1对于衰退函数 这里直接使用"field_value_factor")
以及因为有时候不同函数对分数的影响是可取的,不同函数生成的分数可以通过用户自定义的"weight"进行调整。
"weight"可以在"functions"数组中的每个方法进行定义(如下例子),乘以由各自函数计算的分数.
如果没有任何一个函数声明给定权重,"weight" 充当函数的功能简单的返回这个"weight".
假设"score_mode" 设置为"avg",通过加权的平均数组合成单独的分数。例如,
如果两个函数返回分数1和2,它们各自的权重分别是3和4,然后它们的分数将会
由下列的组合(1*3+2*4)/(3+4)而不是(1*3+2*4)/2.
新的分数将会严格限制不能超出指定的限制通过设置"max_boost"参数。默认的"max_boost"是FLT_MAX
新计算生成的分数是通过查询分数组成。参数"boost_mode"定义如下:
- multiply 查询分数*函数分数相乘(默认)
- replace 只使用函数的分数,忽略查询的分数
- sum 查询分数和函数分数相加
- avg 平均
- max 取查询分数和函数分数中大的
- min 取查询分数和函数分数中小的
默认情况下,修改分数并不改变文档的匹配,排除那些达不到指定分数的文档,可以通过设置"min_score"参数来
设置想要分数阈值。
注意:为了"min_score"工作,所有查询返回的文档必须被打分,并被一个又一个的过滤。
"function_score"查询提供了各种评分函数如下:
- script_score
- weight
- random_score
- field_value_factor
- decay_functions:gauss,linear,exp
script_score
"script_score"函数允许您包装另一个查询,并可选地自定义它的得分,使用一个脚本表达式从doc中获得的其他数值字段值。
下面是简单的demo:
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d' { "query": { "function_score": { "query": { "match": { "message": "elasticsearch" } }, "script_score" : { "script" : { "source": "Math.log(2 + doc['likes'].value)" } } } } } '
除了不同的脚本字段值和表达式之外,"_score"脚本参数还可以用于根据包装查询检索分数。
脚本编译缓存起来用于更快速地执行。如果脚本有需要考虑的参数,最好是重用相同的脚本,
并为其提供参数。
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d' { "query": { "function_score": { "query": { "match": { "message": "elasticsearch" } }, "script_score" : { "script" : { "params": { "a": 5, "b": 1.2 }, "source": "params.a / Math.pow(params.b, doc['likes'].value)" } } } } } '
注意:这并不像"custom_score"查询,查询的分数和脚本评分的结果相乘。如果你想禁止它,设置"boost_mode":"replace".
Weight
"weight"分数允许你将分数乘以锁提供的权重。这有时是需要的,因为在特定查询上的boost值被规范化了,而对于这个分数函数
来说,它没有。数字的值是浮点类型。
"weight" : number
Random
"random_score"产生的分数是均匀分布在[0,1]之间。默认的,它使用了内部Luncene doc id来作为随机性的来源,这是分厂有效的,
但不幸的是不能复制,因为文档可能会被合并重新编号。
如果你希望分数是可复制的,那么就有可能提供一个seed和一个field。
最后的分数将根据这个seed计算出来,被考虑的文档的最小值和根据索引名称和碎片id计算的salt,以便具有相同值但存储在不同索引中的文档得到不同的分数.
注意,在相同的碎片中,在field中具有相同值的文档会得到相同的分数,所以通常需要使用一个对所有文档都有唯一值的field.
一个好的默认选择可能是使用"_seq_no"字段,它唯一的缺点是如果文档被更新,那么分数将会改变,因为更新操作也会更新"_seq_no"字段的值。
注意:在不设置field的情况下设置seed是可能的,但是这已经被弃用了,因为这需要在"_id"字段上加载fielddata,它会消耗大量的内存。
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d' { "query": { "function_score": { "random_score": { "seed": 10, "field": "_seq_no" } } } } '
Field Value factor
"field_value_factor"函数允许你从一个文档中使用一个字段来影响分数,和使用"script_score"函数类似,
但是,它避免了脚本的开销。如果在多值字段中使用,则只在计算中使用字段的第一个值。
举个例子,假设您有一个用数字"likes"字段索引的文档,并希望通过这个字段来影响文档的分数,这样做的示例看起来是这样的:
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d' { "query": { "function_score": { "field_value_factor": { "field": "likes", "factor": 1.2, "modifier": "sqrt", "missing": 1 } } } } '
这将转化为以下的得分公式:
sqrt(1.2 * doc['likes'].value)
对于"field_value_factor"函数有许多选项:
field 从文档中提取的字段。
factor 使字段值与字段值相乘的可选因子,默认是1
modifier 应用于字段值得修改器,可以是如下几种:: none, log, log1p, log2p, ln, ln1p, ln2p, square, sqrt, or reciprocal. 默认是 none.
Decay functions
衰减函数为一个文档提供一个函数,该函数根据给定来源的用户的数字字段值的距离而衰减。
未完待续...