我们为ElasticSearch实例提供了一个特定的用例:我们存储的文档包含专有名称,出生日期,地址,ID号以及其他相关信息。
我们使用一个名称匹配插件,该插件将覆盖ES的默认评分,并根据名称的匹配程度在0和1之间分配一个相关性得分。
我们需要做的是,如果其他字段匹配,则将分数提高一定数量。我已经开始阅读ES脚本来实现此目的。我需要查询脚本部分的帮助。现在,我们的查询如下所示:
{
"size":100,
"query":{
"bool":{
"should":[
{"match":{"Name":"John Smith"}}
]
}
},
"rescore":{
"window_size":100,
"query":{
"rescore_query":{
"function_score":{
"doc_score":{
"fields":{
"Name":{"query_value":"John Smith"},
"DOB":{
"function":{
"function_score":{
"script_score":{
"script":{
"lang":"painless",
"params":{
"query_value":"01-01-1999"
},
"inline":"if **<HERE'S WHERE I NEED ASSISTANCE>**"
}
}
}
}
}
}
}
}
},
"query_weight":0.0,
"rescore_query_weight":1.0
}
}
在查询中始终需要Name
字段,它是分数的基础,分数在默认的_score
字段中返回;为了便于演示,我们将仅添加一个附加字段DOB
,如果该字段匹配,应将得分提高0.1。我相信我正在寻找类似于if(query_value == doc['DOB'].value add 0.1 to _score)
的东西,或者类似于这些东西的东西。
那么,要实现此目标,在inline
行中输入什么正确的语法 ?或者,如果查询需要其他语法修订,请提出建议。
编辑#1-重要的是要强调我们的DOB
字段是text
字段,而不是date
字段。
答案 0 :(得分:1)
假设每个附加字段具有静态权重,则无需使用脚本即可完成此操作(尽管您可能需要使用script_score
来进行更复杂的加权)。为了解决直接添加到文档原始分数的问题,您的评分查询将需要是一个功能分数查询,该查询:
should
子句中为功能得分的主要query
组成对其他字段的查询(即-仅会为与至少一个其他字段匹配的文档产生得分)filter
设置为选择该字段具有某些值的文档,而weight
指定分数应增加多少(或其他评分功能,如果所需的)为示例起见,添加一个State
和DOB
字段(确保多个其他字段正确地贡献了分数)
PUT _template/employee_template
{
"index_patterns": ["employee"],
"settings": {
"number_of_shards": 1
},
"mappings": {
"_doc": {
"properties": {
"Name": {
"type": "text"
},
"State": {
"type": "keyword"
},
"DOB": {
"type": "date"
}
}
}
}
}
POST /employee/_doc/_bulk
{"index":{}}
{"Name": "John Smith", "State": "NY", "DOB": "1970-01-01"}
{"index":{}}
{"Name": "John C. Reilly", "State": "CA", "DOB": "1965-05-24"}
{"index":{}}
{"Name": "Will Ferrell", "State": "FL", "DOB": "1967-07-16"}
编辑:查询已更新,以将原始查询包含在新功能评分中,以补偿自定义评分插件。
有关以下查询的一些注意事项:
score_mode: max
实际上是replace
,因为新计算的功能分数应仅大于或等于原始分数query_weight
和rescore_query_weight
都设置为1
,以便在score_mode: max
比较期间以相等的比例对它们进行比较function_score
查询中:
score_mode: sum
将来自functions
的所有分数加起来boost_mode: sum
将functions
的总和加到query
的分数中POST /employee/_search
{
"size": 100,
"query": {
"bool": {
"should": [
{
"match": {
"Name": "John"
}
},
{
"match": {
"Name": "Will"
}
}
]
}
},
"rescore": {
"window_size": 100,
"query": {
"rescore_query": {
"function_score": {
"query": {
"bool": {
"should": [
{
"match": {
"Name": "John"
}
},
{
"match": {
"Name": "Will"
}
}
],
"filter": {
"bool": {
"should": [
{
"term": {
"State": "CA"
}
},
{
"range": {
"DOB": {
"lte": "1968-01-01"
}
}
}
]
}
}
}
},
"functions": [
{
"filter": {
"term": {
"State": "CA"
}
},
"weight": 0.1
},
{
"filter": {
"range": {
"DOB": {
"lte": "1968-01-01"
}
}
},
"weight": 0.3
}
],
"score_mode": "sum",
"boost_mode": "sum"
}
},
"score_mode": "max",
"query_weight": 1,
"rescore_query_weight": 1
}
}
}
答案 1 :(得分:1)
因为这可以解决问题的方式有所不同(即-按照OP建议使用script_score
而不是尝试从脚本中重写)。
假设与the previous answer的映射和数据相同,查询的脚本版本可能如下所示:
POST /employee/_search
{
"size": 100,
"query": {
"bool": {
"should": [
{
"match": {
"Name": "John"
}
},
{
"match": {
"Name": "Will"
}
}
]
}
},
"rescore": {
"window_size": 100,
"query": {
"rescore_query": {
"function_score": {
"query": {
"bool": {
"should": [
{
"match": {
"Name": "John"
}
},
{
"match": {
"Name": "Will"
}
}
]
}
},
"functions": [
{
"script_score": {
"script": {
"source": "double boost = 0.0; if (params['_source']['State'] == 'FL') { boost += 0.1; } if (params['_source']['DOB'] == '1965-05-24') { boost += 0.3; } return boost;",
"lang": "painless"
}
}
}
],
"score_mode": "sum",
"boost_mode": "sum"
}
},
"query_weight": 0,
"rescore_query_weight": 1
}
}
}
有关脚本的两个注释:
params['_source'][field_name]
访问文档,这是访问文本字段的唯一方法。这非常慢,因为它需要直接访问磁盘上的文档,尽管在重新标记的情况下这种代价可能不会太糟。如果字段是可聚合的类型,例如doc[field_name].value
,keyword
或数字date
此处的DOB
直接与字符串进行比较。这是可能的,因为我们使用的是_source
字段,并且文档的JSON具有指定为字符串的日期。这有点脆弱,但可能会解决问题