使用脚本来提供返回的文档的自定义评分
script_score查询非常有用,例如:评分函数成本比较高且我们仅仅需要计算一组过滤后的文档的评分。
请求的样例:
下面的script_score查询设置每个返回的文档的评分等于my-int字段值除以10
GET /_search
{
"query": {
"script_score": {
"query": {
"match": { "message": "elasticsearch" }
},
"script": {
"source": "doc['my-int'].value / 10 "
}
}
}
}
script_score的顶级参数:
query:必须存在,定义查询对象,用于查询需要返回的文档
script:必须存在,定义脚本对象,脚本用于计算查询返回的文档的评分
(备注:通过script_score查询计算的相关评分不能是负数,为了支持特定的查询优化,Lucene要求评分必须是正数或者0)
min_score:可选,浮点型数值,当文档的评分低于此值的时候,将会被从搜索的结果中排除
boost:可选,浮点型数值,脚本生成的文档分数乘以此值生成最终文档分数。默认值为1.0
注意事项:
在脚本里面使用相关性评分
可以在脚本里面使用_score变量来获取当前文档的相关性评分
预定义函数
我们可以在脚本里面使用任何可用的painless函数,也看有使用下列的预定义函数来自定义评分:
- Saturation
- Sigmoid
- Random score function
- Decay functions for numeric fields
- Decay functions for geo fields
- Decay functions for date fields
- Functions for vector fields
官方建议使用这些预定义函数而不是我们自己来写,这些函数ES官方进行了相关优化。
Saturation
saturation(value,k) = value/(k + value)
"script" : {
"source" : "saturation(doc['my-int'].value, 1)"
}
Sigmoidedit
sigmoid(value, k, a) = value^a/ (k^a + value^a)
"script" : {
"source" : "sigmoid(doc['my-int'].value, 2, 1)"
}
随机评分函数
random_score函数生成从0到但不包括1的均匀分布分数。
random_score函数遵循这样的语法:randomScore(<seed>, <fieldName>)。seed是必须要有的参数,seed是一个整数值,filedName是一个可选参数,是字符型参数。
"script" : {
"source" : "randomScore(100, '_seq_no')"
}
如果filedName参数没有设置,则在内部,Lucene文档的id将会被使用。这将会非常高效,但不幸的是,由于文档可能会被合并重新编号,因此无法复用。
"script" : {
"source" : "randomScore(100)"
}
需要注意的是,位于同一分片中且字段值相同的文档将获得相同的分数,因此通常需要使用一个字段,该字段对整个分片中的所有文档都具有唯一的值。一个好的默认选择可能是使用_seq_no字段,它唯一的缺点是,如果文档被更新,分数将发生变化,因为更新操作也会更新_seq_no字段的值。
数值字段的衰减函数
double decayNumericLinear(double origin, double scale, double offset, double decay, double docValue)
double decayNumericExp(double origin, double scale, double offset, double decay, double docValue)
double decayNumericGauss(double origin, double scale, double offset, double decay, double docValue)
"script" : {
"source" : "decayNumericLinear(params.origin, params.scale, params.offset, params.decay, doc['dval'].value)",
"params": {
"origin": 20,
"scale": 10,
"decay" : 0.5,
"offset" : 0
}
}
备注:脚本只会编译一次,即使后续params会改变。
GEO字段的衰减函数
double decayGeoLinear(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)
double decayGeoExp(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)
double decayGeoGauss(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)
"script" : {
"source" : "decayGeoExp(params.origin, params.scale, params.offset, params.decay, doc['location'].value)",
"params": {
"origin": "40, -70.12",
"scale": "200km",
"offset": "0km",
"decay" : 0.2
}
}
date字段的衰减函数
double decayDateLinear(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)
double decayDateExp(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)
double decayDateGauss(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)
"script" : {
"source" : "decayDateGauss(params.origin, params.scale, params.offset, params.decay, doc['date'].value)",
"params": {
"origin": "2008-01-01T01:00:00Z",
"scale": "1h",
"offset" : "0",
"decay" : 0.5
}
}
备注:日期衰减函数仅仅支持默认的日期格式和默认的时区,此外,不支持使用 now进行计算。
向量类型的函数:
CSDN
开启耗时的脚本查询
如果search.allow_expensive_queries参数被设置为false,则脚本查询不会被执行。
执行计划:
通过explain请求可以看到评分是如何计算的。
script_score查询可以通过设置explanation参数来查看它的执行计划:
GET /my-index-000001/_explain/0
{
"query": {
"script_score": {
"query": {
"match": { "message": "elasticsearch" }
},
"script": {
"source": """
long count = doc['count'].value;
double normalizedCount = count / 10;
if (explanation != null) {
explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);
}
return normalizedCount;
"""
}
}
}
}
执行_search请求时explanation 的值是null,为了避免出现空指针的情况,需要增加一下判断。