欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Elasticsearch复合查询—function_score查询

程序员文章站 2022-07-05 07:50:43
...

function_score允许您修改通过查询检索的文档的分数。例如,如果一个分数函数的计算开销很大,并且它足以在一组经过筛选的文档上计算分数,那么这就很有用。

要使用function_score,用户必须定义一个查询和一个或多个函数,这些函数为查询返回的每个文档计算一个新分数。

function_score只能用于一个这样的函数:

{
  "query": {
    "function_score": {
      "query": { "match_all": {} },
      "boost": "5",
      "random_score": {}, 
      "boost_mode": "multiply"
    }
  }
}

此外,可以组合几个函数。在这种情况下,只有当文档与给定的筛选查询匹配时,才可以选择应用该函数

{
  "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,是任意的),而且有时希望函数对分数有不同的影响,所以每个函数的分数可以根据用户定义的权重进行调整。权重可以在functions数组(上面的例子)中为每个函数定义,并与各自函数计算的分数相乘。如果权重是在没有任何其他函数声明的情况下给出的,那么权重就充当一个函数,它只是返回权重。

在score_mode设置为avg的情况下,单个分数将通过加权平均合并。例如,如果两个函数返回score 1和2,它们各自的权重为3和4,那么它们的得分将被合并为(13+24)/(3+4)而不是(13+24)/2。

通过设置max_boost参数,可以限制新分数不超过某个限制。max_boost的默认值是FLT_MAX。

新计算的分数与查询的分数结合在一起。参数boost_mode定义如下:

参数值 含义
multiply 查询分数和函数分数相乘(默认)
replace 只使用函数分数,查询分数被忽略
sum 查询分数和函数分数之和
avg 平均值
max 最大值
min 最小值

默认情况下,修改分数不会改变匹配的文档。为了排除不满足某个分数阈值的文档,可以将min_score参数设置为所需的分数阈值。

为了使min_score正常工作,需要对查询返回的所有文档进行评分,然后逐一过滤掉。

function_score查询提供如下几种类型的得分函数

脚本分数

script_score函数允许您包装另一个查询,并使用脚本表达式从文档中的其他数值字段值计算可选地定制它的评分。这里有一个简单的例子:

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

在Elasticsearch中,所有文档分数都是正的32位浮点数。

如果script_score函数生成更精确的分数,它将被转换为最接近的32位浮点数。

同样,分数必须是非负的。否则,Elasticsearch将返回一个错误。

在不同的脚本字段值和表达式之上,可以使用_score脚本参数根据包装好的查询检索分数。

缓存脚本编译以更快地执行。如果脚本有需要考虑的参数,最好重用相同的脚本,并提供参数给它:

{
  "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)"
        }
      }
    }
  }
}

注意,与custom_score查询不同,查询的分数与脚本评分的结果相乘。如果你想阻止它,设置"boost_mode": "replace"

权重

权重分数允许您将该分数乘以所提供的权重。这有时是需要的,因为特定查询上的boost值设置被规范化了,而这个score函数却没有。数值的类型为float。

"weight" : number

随机

random_score生成从0到但不包括1均匀分布的分数。默认情况下,它使用内部Lucene doc id作为随机性的来源,这是非常有效的,但不幸的是不能重现,因为文档可能会通过合并重新编号。

如果您希望分数是可重复的,可以提供一个seed和field。最后的分数将会基于seed被计算,所考虑的文档的最小字段值和基于索引名和碎片id计算的salt,因此,具有相同值但存储在不同索引中的文档得到不同的分数。请注意,在相同的碎片中并且对field具有相同值的文档将获得相同的分数,因此通常需要使用对所有文档具有惟一值的字段。一个好的缺省选择可能是使用_seq_no字段,它的唯一缺点是,如果文档被更新,分数会发生变化,因为更新操作也会更新_seq_no字段的值。

可以在不设置字段的情况下设置种子,但是这已经被否决了,因为这需要在_id字段上加载fielddata,这会消耗大量内存。

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

字段值引用

field_value_factor函数允许您使用文档中的一个字段来影响分数。它类似于使用script_score函数,但是它避免了脚本开销。如果在多值字段上使用,则只在计算中使用字段的第一个值。

例如,假设您有一个使用数值my-int字段索引的文档,并且希望通过该字段影响文档的得分,这样做的示例如下:

{
  "query": {
    "function_score": {
      "field_value_factor": {
        "field": "my-int",
        "factor": 1.2,
        "modifier": "sqrt",
        "missing": 1
      }
    }
  }
}

它将转化为以下得分公式:sqrt(1.2 * doc['my-int'].value)

field_value_factor函数有许多选项:

选项 描述
field 要从文档中提取的字段。
factor 字段值相乘的可选因子,默认值为1。
modifier 修饰符应用到字段值,可以是:none, log, log1p, log2p, ln, ln1p, ln2p, square, sqrt,或倒数。默认为none。
modifier 含义
none 不应用任何乘数到字段值
log 取字段值的公共对数。因为这个函数将返回一个负值,如果在0到1之间使用,会导致错误,所以建议使用log1p。
log1p 在字段值上加1,然后取公共对数
log2p 在字段值上加2,然后取公共对数
ln 取字段值的自然对数。由于该函数将返回一个负值,如果用于0到1之间的值,则会导致错误,因此建议使用ln1p
ln1p 对字段值加1并取自然对数
ln2p 对字段值加2并取自然对数
square 将字段值平方(与自身相乘)
sqrt 取字段值的平方根
reciprocal 倒数

missing: 如果文档没有该字段,则使用。修改器和因子仍然应用于它,就像它是从文档中读取的一样。

field_value_score函数生成的分数必须是非负的,否则将抛出错误。如果对0到1之间的值使用log和ln修饰符,则会产生负值。一定要使用范围过滤器限制字段的值以避免这种情况,或者使用log1p和ln1p。

请记住,取log()为0或负数的平方根是不合法的操作,并且会抛出异常。一定要使用范围过滤器限制字段的值以避免这种情况,或者使用log1p和ln1p。