您应该尝试使用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,因为他们的性能很差。