【问题标题】:Using nested values in script_score在 script_score 中使用嵌套值
【发布时间】:2020-07-29 00:07:17
【问题描述】:

我正在尝试在脚本得分中使用嵌套值,但我在使其正常工作时遇到问题,因为我无法通过 doc 访问该字段来对其进行迭代。此外,当我尝试在 Kibana 中像 _type:images AND _exists_:colors 一样查询它时,它不会匹配任何文档,即使当我单独查看该字段时,我的所有文档中都清楚地存在该字段。不过,我可以使用 params._source 访问它,但我读到它可能会很慢而且不推荐。

我知道这个问题完全是由于我们创建这个嵌套字段的方式,所以如果我不能想出比这更好的东西,我将不得不重新索引我们的 2m+ 文档,看看我是否能找到另一种解决方法问题,但我想避免这种情况,也只是更好地了解 Elastic 在幕后是如何工作的,以及为什么它会以这里的方式运行。

我将在这里提供的示例不是我现实生活中的问题,但也描述了这个问题。 想象一下,我们有一个描述图像的文档。该文档有一个字段,其中包含图像中存在多少红色、蓝色和绿色的值。

使用嵌套字段创建索引和文档的请求,其中包含颜色数组,它们之间有 100 点分割:

PUT images
{
  "settings": {
    "number_of_shards": 1
  },
  "mappings": {
    "_doc": {
      "properties": {
        "id" : { "type" : "integer" },
        "title" : { "type" : "text" },
        "description" : { "type" : "text" },
        "colors": {
          "type": "nested",
          "properties": {
            "red": {
              "type": "double"
            },
            "green": {
              "type": "double"
            },
            "blue": {
              "type": "double"
            }
          }
        }
      }
    }
  }
}

PUT images/_doc/1
{
    "id" : 1,
    "title" : "Red Image",
    "description" : "Description of Red Image",
    "colors": [
      {
        "red": 100
      },
      {
        "green": 0
      },
      {
        "blue": 0
      }
    ]
}

PUT images/_doc/2
{
    "id" : 2,
    "title" : "Green Image",
    "description" : "Description of Green Image",
    "colors": [
      {
        "red": 0
      },
      {
        "green": 100
      },
      {
        "blue": 0
      }
    ]
}

PUT images/_doc/3
{
    "id" : 3,
    "title" : "Blue Image",
    "description" : "Description of Blue Image",
    "colors": [
      {
        "red": 0
      },
      {
        "green": 0
      },
      {
        "blue": 100
      }
    ]
}

现在,如果我使用 doc 运行此查询:

GET images/_search
{
  "query": {
    "function_score": {
      "functions": [
        {
          "script_score": {
            "script": {
              "source": """
                boolean debug = true;
                for(color in doc["colors"]) {
                  if (debug === true) {
                    throw new Exception(color["red"].toString());
                  }
                }
              """
            }
          }
        }
      ]
    }
  }
}

我会得到异常 No field found for [colors] in mapping with types [],但如果我改用 params._source,就像这样:

GET images/_search
{
  "query": {
    "function_score": {
      "functions": [
        {
          "script_score": {
            "script": {
              "source": """
                boolean debug = true;
                for(color in params._source["colors"]) {
                  if (debug === true) {
                    throw new Exception(color["red"].toString());
                  }
                }
              """
            }
          }
        }
      ]
    }
  }
}

我能够输出"caused_by": {"type": "exception", "reason": "100"},所以我知道它有效,因为第一个文档是红色的并且值为 100。

我什至不确定这是否可以归类为问题,但更多的是寻求帮助。如果有人能解释为什么会这样,并给出解决问题的最佳方法的想法,我将不胜感激。

(另外,在 Painless 中调试的一些技巧也很可爱!!!)

【问题讨论】:

    标签: elasticsearch lucene kibana elasticsearch-painless


    【解决方案1】:

    在 Elasticsearch 的评分脚本 "script_score": {"script": {"source": "..." }} 中,您可以使用 param._source 对象访问嵌套值。

    例如,如果您的 documents 索引包含以下文档:

    {
      "title": "Yankees Potential Free Agent Target: Max Scherzer",
      "body": "...",
      "labels": {
        "genres": "news",
        "topics": ["sports", "celebrities"]
        "publisher": "CNN"
      }
    }
    

    以下查询将按随机顺序返回 100 个文档,优先选择主题为 sports 的文档:

    GET documents/_search
    {
      "size": 100,
      "sort": [
        "_score"
      ],
      "query": {
        "function_score": {
          "query": { "match_all": {} },
          "functions": [
            {
              "random_score": {}
            },
            {
              "script_score": {
                "script": {
                  "source": """
                    double boost = 1.0;
                    if (params._source['labels'] != null && params._source['labels']['topics'] != null && params._source['labels']['topics'].contains('sports') {
                        boost += 2.0;
                    }
                    return boost;
                  """
                }
              }
            }
          ],
          "score_mode": "multiply",
          "boost_mode": "replace"
        }
      }
    }
    

    【讨论】:

      【解决方案2】:

      不用担心params._source 的缓慢性——这是您唯一的选择,因为迭代doc 的嵌套上下文只允许访问单个嵌套颜色。

      试试这个:

      GET images/_search
      {
        "query": {
          "bool": {
            "must": [
              {
                "match": {
                  "title": "image"
                }
              },
              {
                "function_score": {
                  "functions": [
                    {
                      "script_score": {
                        "script": {
                          "source": """
                              def score = 0;
                              for (color in params._source["colors"]) {
                                // Debug.explain(color);
                                if (color.containsKey('red')) {
                                  score += color['red'] ;
                                }
                              }
                              return score;
                          """
                        }
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      }
      

      无痛评分上下文是here

      其次,您非常接近手动抛出异常 - 但有一种更简洁的方法可以做到这一点。取消注释Debug.explain(color);,您就可以开始了。

      还有一件事,我特意添加了一个match 查询来提高分数,但更重要的是,为了说明如何在后台构建查询——当您在GET images/_validate/query?explain 下重新运行上述内容时,您会看到为自己。

      【讨论】:

      • 这个@SeverinDK 运气好吗?
      猜你喜欢
      • 1970-01-01
      • 2020-09-25
      • 1970-01-01
      • 1970-01-01
      • 2015-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多