【问题标题】:How to call stored painless script function in elastisearch如何在elasticsearch中调用存储的无痛脚本函数
【发布时间】:2019-08-31 15:21:03
【问题描述】:

我正在尝试使用来自

的示例

https://www.elastic.co/guide/en/elasticsearch/reference/6.4/modules-scripting-using.html

我创建了一个函数并保存了它。

POST http://localhost:9200/_scripts/calculate-score
{
  "script": {
    "lang": "painless",
    "source": "ctx._source.added + params.my_modifier"
  }
}

尝试调用保存的函数

POST http://localhost:9200/users/user/_search
{
  "query": {
    "script": {
      "script": {
        "id": "calculate-score",
        "params": {
          "my_modifier": 2
        }
      }
    }
  }
}

它返回一个错误:Variable [ctx] is not defined。我尝试使用doc['added'],但收到了同样的错误。请帮助我了解如何调用该函数。

【问题讨论】:

    标签: function elasticsearch elasticsearch-painless


    【解决方案1】:

    您应该尝试使用doc['added'].value,让我解释一下为什么以及如何使用。简而言之,因为无痛脚本语言相当简单但晦涩难懂。

    为什么ES找不到ctx变量?

    它找不到ctx 变量的原因是因为这个无痛脚本在"filter context" 中运行,并且这样的变量在过滤器上下文中不可用。 (如果你好奇的话,从 ES 6.4 开始就有 18 types of painless context)。

    在过滤器上下文中只有两个可用的变量:

    params(地图,只读)

    作为查询的一部分传入的用户定义参数。

    doc(地图,只读)

    包含当前文档的字段,其中每个字段都是一个值列表。

    在您的情况下使用 doc['added'].value 应该足够了:

    POST /_scripts/calculate-score
    {
      "script": {
        "lang": "painless",
        "source": "doc['added'].value + params.my_modifier"
      }
    }
    

    应该,因为如果我们尝试执行它会出现另一个问题(就像你做的那样):

          "type": "script_exception",
          "reason": "runtime error",
          "script_stack": [
            "doc['added'].value + params.my_modifier",
            "^---- HERE"
          ],
          "script": "calculate-score",
          "lang": "painless",
          "caused_by": {
            "type": "class_cast_exception",
            "reason": "cannot cast def [long] to boolean"
          }
    

    由于它的context,这个脚本预计会返回一个boolean

    返回

    boolean

    返回 true 如果当前文档应该作为一个返回 查询结果,否则false

    此时我们可以理解为什么您尝试执行的脚本对 Elasticsearch 没有多大意义:它应该判断文档是否与 script query 匹配。如果脚本返回一个整数,Elasticsearch 将不知道它是true 还是false

    如何使存储的脚本在过滤器上下文中工作?

    作为示例,我们可以使用以下脚本:

    POST /_scripts/calculate-score1
    {
      "script": {
        "lang": "painless",
        "source": "doc['added'].value > params.my_modifier"
      }
    }
    

    现在我们可以访问脚本了:

    POST /users/user/_search
    {
      "query": {
        "script": {
          "script": {
            "id": "calculate-score1",
            "params": {
              "my_modifier": 2
            }
          }
        }
      }
    }
    

    它会返回所有added大于2的文档:

    "hits": [
      {
        "_index": "users",
        "_type": "user",
        "_id": "1",
        "_score": 1,
        "_source": {
          "name": "John Doe",
          "added": 40
        }
      }
    ]
    

    这次脚本返回了boolean,而 Elasticsearch 设法使用了它。

    如果您好奇,range 查询可以完成同样的工作,无需编写脚本。

    为什么我必须在doc['added'] 后面加上.value

    如果您尝试直接访问doc['added'],您可能会注意到错误消息不同:

    POST /_scripts/calculate-score
    {
      "script": {
        "lang": "painless",
        "source": "doc['added'] + params.my_modifier"
      }
    }
    
          "type": "script_exception",
          "reason": "runtime error",
          "script_stack": [
            "doc['added'] + params.my_modifier",
            "                     ^---- HERE"
          ],
          "script": "calculate-score",
          "lang": "painless",
          "caused_by": {
            "type": "class_cast_exception",
            "reason": "Cannot apply [+] operation to types [org.elasticsearch.index.fielddata.ScriptDocValues.Longs] and [java.lang.Integer]."
          }
    

    painless 再一次向我们展示了它的晦涩之处:当访问文档的字段 'added' 时,我们获得了一个 org.elasticsearch.index.fielddata.ScriptDocValues.Longs 的实例,Java 虚拟机拒绝将其与整数相加(我们不能在这里责怪 Java) .

    所以我们必须实际调用.getValue() 方法,简单翻译为.value

    如果我想更改文档中的该字段怎么办?

    如果您想在某个文档的added 字段中添加2,并保存更新后的文档怎么办? Update API 可以做到这一点。

    它在update context 中运行,实际上已经定义了ctx 变量,而该变量又可以通过ctx['_source'] 访问原始JSON 文档。

    我们可能会创建一个新脚本:

    POST /_scripts/add-some
    {
      "script": {
        "lang": "painless",
        "source": "ctx['_source']['added'] += params.my_modifier"
      }
    }
    

    现在我们可以使用它了:

    POST /users/user/1/_update
    {
        "script" : {
            "id": "add-some",
            "params" : {
                "my_modifier" : 2
            }
        }
    }
    

    为什么文档中的示例不起作用?

    显然,因为它是错误的。这个脚本(来自这个documentation page):

    POST _scripts/calculate-score
    {
      "script": {
        "lang": "painless",
        "source": "Math.log(_score * 2) + params.my_modifier"
      }
    }
    

    稍后在过滤器上下文中执行(在搜索请求中,在 script 查询中),并且,正如我们现在所知,没有可用的 _score 变量。

    这个脚本只有在score context 中才有意义,当运行funtion_score query 允许调整文档的相关性分数时。

    最后说明

    我想提一下,总的来说,推荐avoid using scripts,因为他们的性能很差。

    【讨论】:

    • 想了很多。这是很好的澄清。我还有一个问题。如何在存储的脚本中进行聚合?是否可以?那么如果我使用查询而不是存储脚本会更好吗?
    • @Roman 如果您认为存储脚本类似于 SQL 中的存储过程,那么通过脚本是不可能的。与存储过程最相似的是search templates。我鼓励您继续尝试 Elasticsearch,您会发现最适合的!我认为在大多数情况下可以避免使用脚本。
    • 我们如何从三引号多行无痛返回一个值?
    • @OliverDixon This answer 可能会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 2022-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 2017-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多