ElasticSearch-使用脚本提高得分

时间:2019-07-10 13:55:03

标签: elasticsearch elasticsearch-painless

我们为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字段。

2 个答案:

答案 0 :(得分:1)

假设每个附加字段具有静态权重,则无需使用脚本即可完成此操作(尽管您可能需要使用script_score来进行更复杂的加权)。为了解决直接添加到文档原始分数的问题,您的评分查询将需要是一个功能分数查询,该查询:

  1. should子句中为功能得分的主要query组成对其他字段的查询(即-仅会为与至少一个其他字段匹配的文档产生得分)
  2. 每个附加字段使用一个功能,filter设置为选择该字段具有某些值的文档,而weight指定分数应增加多少(或其他评分功能,如果所需的)

映射(作为模板)

为示例起见,添加一个StateDOB字段(确保多个其他字段正确地贡献了分数)

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_weightrescore_query_weight都设置为1,以便在score_mode: max比较期间以相等的比例对它们进行比较
  • function_score查询中:
    • score_mode: sum将来自functions的所有分数加起来
    • boost_mode: sumfunctions的总和加到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
    }
  }
}

有关脚本的两个注释:

  1. 脚本使用params['_source'][field_name]访问文档,这是访问文本字段的唯一方法。这非常慢,因为它需要直接访问磁盘上的文档,尽管在重新标记的情况下这种代价可能不会太糟。如果字段是可聚合的类型,例如doc[field_name].valuekeyword或数字
  2. ,则可以改用date 此处的
  3. DOB直接与字符串进行比较。这是可能的,因为我们使用的是_source字段,并且文档的JSON具有指定为字符串的日期。这有点脆弱,但可能会解决问题