首页 > 解决方案 > ElasticSearch - 使用脚本提高分数

问题描述

我们的 ElasticSearch 实例有一个特定的用例:我们存储包含专有名称、出生日期、地址、身份证号和其他相关信息的文档。

我们使用了一个名称匹配插件,它会覆盖 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领域。

标签: elasticsearchelasticsearch-painless

解决方案


拆分为单独的答案,因为这以不同的方式解决了问题(即 - 通过使用script_score建议的 OP 而不是尝试重写脚本)。

假设与上一个答案相同的映射和数据,查询的脚本版本可能如下所示:

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].value,例如keyword,date或数字
  2. DOB这里直接与字符串进行比较。这是可能的,因为我们正在使用该_source字段,并且文档的 JSON 将日期指定为字符串。这有点脆弱,但可能会成功

推荐阅读