函数评分查询
编辑函数评分查询编辑
function_score
允许您修改查询检索到的文档的评分。例如,如果评分函数计算量很大,并且仅对过滤后的文档集进行评分就足够了,这将非常有用。
要使用 function_score
,用户必须定义一个查询和一个或多个函数,这些函数计算查询返回的每个文档的新评分。
function_score
可以像这样只使用一个函数
resp = client.search( body={ "query": { "function_score": { "query": {"match_all": {}}, "boost": "5", "random_score": {}, "boost_mode": "multiply", } } }, ) print(resp)
response = client.search( body: { query: { function_score: { query: { match_all: {} }, boost: '5', random_score: {}, boost_mode: 'multiply' } } } ) puts response
res, err := es.Search( es.Search.WithBody(strings.NewReader(`{ "query": { "function_score": { "query": { "match_all": {} }, "boost": "5", "random_score": {}, "boost_mode": "multiply" } } }`)), es.Search.WithPretty(), ) fmt.Println(res, err)
GET /_search { "query": { "function_score": { "query": { "match_all": {} }, "boost": "5", "random_score": {}, "boost_mode": "multiply" } } }
有关支持函数的列表,请参见 函数评分。 |
此外,可以组合多个函数。在这种情况下,可以选择仅在文档匹配给定过滤查询时才应用函数
resp = client.search( body={ "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, } } }, ) print(resp)
response = client.search( body: { 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 } } } ) puts response
res, err := es.Search( es.Search.WithBody(strings.NewReader(`{ "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 } } }`)), es.Search.WithPretty(), ) fmt.Println(res, err)
GET /_search { "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
指定如何组合计算出的评分
|
评分相乘(默认) |
|
评分相加 |
|
评分取平均值 |
|
应用第一个具有匹配过滤器的函数 |
|
使用最大评分 |
|
使用最小评分 |
由于评分可能处于不同的尺度(例如,衰减函数的评分在 0 到 1 之间,而 field_value_factor
的评分是任意的),并且有时希望函数对评分的影响不同,因此可以使用用户定义的 weight
调整每个函数的评分。可以在 functions
数组(上面的示例)中为每个函数定义 weight
,并将其乘以相应函数计算的评分。如果在没有其他函数声明的情况下给出权重,则 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
定义了
|
查询评分和函数评分相乘(默认) |
|
仅使用函数评分,忽略查询评分 |
|
查询评分和函数评分相加 |
|
平均值 |
|
查询评分和函数评分的最大值 |
|
查询评分和函数评分的最小值 |
默认情况下,修改评分不会改变哪些文档匹配。要排除不满足特定评分阈值的文档,可以将 min_score
参数设置为所需的评分阈值。
要使 min_score
生效,查询返回的 所有 文档都需要进行评分,然后逐个过滤掉。
function_score
查询提供了几种类型的评分函数。
-
script_score
-
weight
-
random_score
-
field_value_factor
-
衰减函数:
gauss
、linear
、exp
脚本评分编辑
script_score
函数允许您包装另一个查询,并使用脚本表达式从文档中的其他数值字段值派生的计算结果来可选地自定义其评分。以下是一个简单的示例
resp = client.search( body={ "query": { "function_score": { "query": {"match": {"message": "elasticsearch"}}, "script_score": { "script": { "source": "Math.log(2 + doc['my-int'].value)" } }, } } }, ) print(resp)
response = client.search( body: { query: { function_score: { query: { match: { message: 'elasticsearch' } }, script_score: { script: { source: "Math.log(2 + doc['my-int'].value)" } } } } } ) puts response
GET /_search { "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
脚本参数来检索基于包装查询的评分。
脚本编译被缓存以加快执行速度。如果脚本需要考虑参数,最好重用相同的脚本,并为其提供参数
resp = client.search( body={ "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)", } }, } } }, ) print(resp)
response = client.search( body: { 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)" } } } } } ) puts response
GET /_search { "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"
权重编辑
weight
评分允许您将评分乘以提供的 weight
。这在某些情况下可能需要,因为特定查询上设置的提升值会被归一化,而对于此评分函数则不会。数值是浮点类型。
"weight" : number
随机编辑
random_score
生成在 0 到 1 之间(不包括 1)均匀分布的评分。默认情况下,它使用内部 Lucene 文档 ID 作为随机性来源,这非常有效,但不幸的是不可重现,因为文档可能会被合并重新编号。
如果您希望评分可重现,则可以提供 seed
和 field
。最终的评分将基于此种子、所考虑文档的 field
的最小值以及基于索引名称和分片 ID 计算的盐值来计算,这样具有相同值但存储在不同索引中的文档会获得不同的评分。请注意,位于同一分片中且具有相同 field
值的文档将获得相同的评分,因此通常希望使用一个对所有文档都具有唯一值的字段。一个好的默认选择可能是使用 _seq_no
字段,其唯一的缺点是,如果文档被更新,评分会发生变化,因为更新操作也会更新 _seq_no
字段的值。
可以设置种子而不设置字段,但这已被弃用,因为这需要在 _id
字段上加载 fielddata,这会消耗大量内存。
resp = client.search( body={ "query": { "function_score": { "random_score": {"seed": 10, "field": "_seq_no"} } } }, ) print(resp)
response = client.search( body: { query: { function_score: { random_score: { seed: 10, field: '_seq_no' } } } } ) puts response
res, err := es.Search( es.Search.WithBody(strings.NewReader(`{ "query": { "function_score": { "random_score": { "seed": 10, "field": "_seq_no" } } } }`)), es.Search.WithPretty(), ) fmt.Println(res, err)
GET /_search { "query": { "function_score": { "random_score": { "seed": 10, "field": "_seq_no" } } } }
字段值因子编辑
field_value_factor
函数允许您使用文档中的字段来影响评分。它类似于使用 script_score
函数,但是它避免了脚本的开销。如果在多值字段上使用,则仅使用字段的第一个值进行计算。
例如,假设您有一个用数值 my-int
字段索引的文档,并且希望使用此字段来影响文档的评分,那么执行此操作的示例如下
resp = client.search( body={ "query": { "function_score": { "field_value_factor": { "field": "my-int", "factor": 1.2, "modifier": "sqrt", "missing": 1, } } } }, ) print(resp)
response = client.search( body: { query: { function_score: { field_value_factor: { field: 'my-int', factor: 1.2, modifier: 'sqrt', missing: 1 } } } } ) puts response
GET /_search { "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
函数有许多选项
|
要从文档中提取的字段。 |
|
可选的因子,用于将字段值相乘,默认为 |
|
要应用于字段值的修饰符,可以是以下之一: |
修饰符 | 含义 |
---|---|
|
不对字段值应用任何乘数 |
|
取字段值的 常用对数。由于此函数将返回负值,并且如果在 0 到 1 之间的数值上使用会导致错误,因此建议使用 |
|
将 1 加到字段值,然后取常用对数 |
|
将 2 加到字段值,然后取常用对数 |
|
取字段值的 自然对数。由于此函数将返回负值,并且如果在 0 到 1 之间的数值上使用会导致错误,因此建议使用 |
|
将 1 加到字段值,然后取自然对数 |
|
将 2 加到字段值,然后取自然对数 |
|
对字段值进行平方(将其自身相乘) |
|
取字段值的 平方根 |
|
取倒数 字段值,与 |
-
missing
- 如果文档没有该字段,则使用该值。修饰符和因子仍然应用于它,就好像它是从文档中读取的一样。
由 field_value_score
函数产生的评分必须是非负的,否则会抛出错误。如果在 0 到 1 之间的数值上使用 log
和 ln
修饰符,则会产生负值。请务必使用范围过滤器来限制字段的值以避免这种情况,或者使用 log1p
和 ln1p
。
请记住,取 0 的 log() 或负数的平方根是非法的操作,会抛出异常。请务必使用范围过滤器来限制字段的值以避免这种情况,或者使用 log1p
和 ln1p
。
衰减函数编辑
衰减函数根据文档中数字字段值与用户给定的原点之间的距离,使用一个衰减函数对文档进行评分。这类似于范围查询,但边缘平滑而不是方框。
要在具有数字字段的查询上使用距离评分,用户必须为每个字段定义一个 origin
和一个 scale
。 origin
用于定义计算距离的“中心点”,而 scale
用于定义衰减率。衰减函数指定为
"DECAY_FUNCTION": { "FIELD_NAME": { "origin": "11, 12", "scale": "2km", "offset": "0km", "decay": 0.33 } }
在上面的示例中,该字段是 geo_point
,并且可以以地理格式提供原点。在这种情况下,必须以单位给出 scale
和 offset
。如果您的字段是日期字段,您可以将 scale
和 offset
设置为天、周等。示例
resp = client.search( body={ "query": { "function_score": { "gauss": { "@timestamp": { "origin": "2013-09-17", "scale": "10d", "offset": "5d", "decay": 0.5, } } } } }, ) print(resp)
response = client.search( body: { query: { function_score: { gauss: { "@timestamp": { origin: '2013-09-17', scale: '10d', offset: '5d', decay: 0.5 } } } } } ) puts response
GET /_search { "query": { "function_score": { "gauss": { "@timestamp": { "origin": "2013-09-17", "scale": "10d", "offset": "5d", "decay": 0.5 } } } } }
原点的日期格式取决于映射中定义的 |
|
|
|
用于计算距离的原点。必须以数字形式给出数字字段,以日期形式给出日期字段,以地理点形式给出地理字段。地理和数字字段需要。对于日期字段,默认值为 |
|
所有类型都需要。定义从原点 + 偏移量到计算出的分数等于 |
|
如果定义了 |
|
|
在第一个示例中,您的文档可能代表酒店,并包含一个地理位置字段。您希望根据酒店距离给定位置的距离来计算衰减函数。您可能无法立即看到为高斯函数选择哪个比例,但您可以说:“在距离目标位置 2 公里的距离处,分数应降至三分之一。”然后会自动调整参数“scale”以确保分数函数计算出距离目标位置 2 公里的酒店的分数为 0.33。
在第二个示例中,字段值介于 2013-09-12 和 2013-09-22 之间的文档将获得 1.0 的权重,而距离该日期 15 天的文档将获得 0.5 的权重。
支持的衰减函数编辑
DECAY_FUNCTION
确定衰减的形状
-
gauss
-
正常衰减,计算为
其中 计算以确保分数在距离
scale
处取值decay
从origin
+-offset
请参阅 正常衰减,关键字
gauss
,以了解演示gauss
函数生成的曲线的图表。 -
exp
-
指数衰减,计算为
其中参数 再次计算以确保分数在距离
scale
处取值decay
从origin
+-offset
请参阅 指数衰减,关键字
exp
,以了解演示exp
函数生成的曲线的图表。 -
linear
-
线性衰减,计算为
.
其中参数
s
再次计算以确保分数在距离scale
处取值decay
从origin
+-offset
与正常衰减和指数衰减相比,此函数实际上将分数设置为 0,如果字段值超过用户给定的比例值的兩倍。
对于单个函数,三个衰减函数及其参数可以这样可视化(此示例中的字段称为“age”)
多值字段编辑
如果用于计算衰减的字段包含多个值,则默认情况下,选择最接近原点的值来确定距离。这可以通过设置 multi_value_mode
来更改。
|
距离是最小距离 |
|
距离是最大距离 |
|
距离是平均距离 |
|
距离是所有距离的总和 |
示例
"DECAY_FUNCTION": { "FIELD_NAME": { "origin": ..., "scale": ... }, "multi_value_mode": "avg" }
详细示例编辑
假设您正在某个城镇搜索酒店。您的预算有限。此外,您希望酒店靠近市中心,因此酒店距离目标位置越远,您入住的可能性就越小。
您希望与您的标准(例如,“酒店、南希、非吸烟者”)匹配的查询结果根据到市中心的距离和价格进行评分。
直观地说,您希望将市中心定义为原点,也许您愿意从酒店步行 2 公里到市中心。
在这种情况下,您对位置字段的 原点 是市中心,而 比例 约为 2 公里。
如果您的预算很低,您可能更喜欢便宜的东西而不是昂贵的东西。对于价格字段, 原点 将为 0 欧元,而 比例 取决于您愿意支付多少,例如 20 欧元。
在此示例中,字段可能称为“price”(酒店的价格)和“location”(酒店的坐标)。
在这种情况下, price
的函数将是
以及 location
假设您想将这两个函数乘以原始分数,则请求将如下所示
resp = client.search( body={ "query": { "function_score": { "functions": [ {"gauss": {"price": {"origin": "0", "scale": "20"}}}, { "gauss": { "location": { "origin": "11, 12", "scale": "2km", } } }, ], "query": {"match": {"properties": "balcony"}}, "score_mode": "multiply", } } }, ) print(resp)
response = client.search( body: { query: { function_score: { functions: [ { gauss: { price: { origin: '0', scale: '20' } } }, { gauss: { location: { origin: '11, 12', scale: '2km' } } } ], query: { match: { properties: 'balcony' } }, score_mode: 'multiply' } } } ) puts response
res, err := es.Search( es.Search.WithBody(strings.NewReader(`{ "query": { "function_score": { "functions": [ { "gauss": { "price": { "origin": "0", "scale": "20" } } }, { "gauss": { "location": { "origin": "11, 12", "scale": "2km" } } } ], "query": { "match": { "properties": "balcony" } }, "score_mode": "multiply" } } }`)), es.Search.WithPretty(), ) fmt.Println(res, err)
GET /_search { "query": { "function_score": { "functions": [ { "gauss": { "price": { "origin": "0", "scale": "20" } } }, { "gauss": { "location": { "origin": "11, 12", "scale": "2km" } } } ], "query": { "match": { "properties": "balcony" } }, "score_mode": "multiply" } } }
接下来,我们将展示如何计算每个可能的衰减函数的分数。
正常衰减,关键字 gauss
编辑
在上述示例中选择 gauss
作为衰减函数时,乘数的等高线图和表面图如下所示
假设您的原始搜索结果匹配三家酒店
- "Backback Nap"
- "Drink n Drive"
- "BnB Bellevue".
"Drink n Drive" 距离您定义的位置很远(接近 2 公里),而且不太便宜(约 13 欧元),因此它获得的因子很低,因子为 0.56。 "BnB Bellevue" 和 "Backback Nap" 都非常靠近您定义的位置,但 "BnB Bellevue" 更便宜,因此它获得的乘数为 0.86,而 "Backpack Nap" 获得的值为 0.66。
衰减函数支持的字段编辑
仅支持数字、日期和地理点字段。
如果字段缺失怎么办?编辑
如果文档中缺少数字字段,则该函数将返回 1。