如何在查询时转换字段的值?如何对文档执行复杂的更新操作?如何在ingest processor中指定执行条件?
针对上述等一系列问题,都可以通过ES脚本来解决,其允许用户在一些特定的API中对自定义表达式进行求值。
先来看一个例子:索引存在一个字段DiskUsage
表示磁盘的使用量,单位是MB,现在希望查询结果展现以GB为单位的值。
这种情况可以使用Script Fields功能:使用脚本对DiskUsage
字段的值进行转换,转换后的值通过新字段DiskUsageInGB
返回。
GET metrics/_search
{
"query": {
"match_all": {}
},
"script_fields": {
"DiskUsageInGB": {
"script": {
"lang": "painless",
"source": "doc['DiskUsage'].value / 1024"
}
}
}
}
在任意ES API中使用脚本都遵循以下格式
"script": {
"lang": "...",
"source" | "id": "...",
"params": { ... }
}
painless
.ES脚本语言包括通用目的语言和特定目的语言两种类型,通用目的语言能在任何允许脚本的API中使用,而特定目的语言则只能在特定API中使用(拥有更高的性能)。
通用目的语言
在6.0之前的版本,ES通用目的语言内置支持painless、groovy以及允许通过安装lang插件的方式支持python和javascript。但在6.0之后的版本则仅支持 Painless,原因可能在于painless专门为ES设计,相比其他通用语言拥有更高的安全性。
Language | Sandboxed | Required plugin |
---|---|---|
yes | built-in | |
built-in | ||
Sandboxed:沙箱语言在设计时考虑了安全因素(比如限制了可以调用的方法),能被用户或应用安全的使用;非沙盒脚本具有对ES内部未经检查的访问权限,滥用可能会导致各种问题,见 Scripting and security
特定目的语言
Language | Sandboxed | Required plugin | Purpose |
---|---|---|---|
yes | built-in | fast custom ranking and sorting | |
yes | built-in | templates |
通常情况下,在API中使用脚本时会需要访问文档中的一些字段或特殊的变量。而在不同类型ES API中使用脚本,其访问方式也有所不同。
在 update, update-by-query, 或 reindex API中使用脚本时,需要通过ctx
去访问文档中的字段。
ctx._source
: 访问文档_source
字段ctx.op
: 对文档对应的操作,包括index
和delete
ctx._index
: 访问文档的 meta-fields除了每次搜索命中执行一次的脚本字段(script fields)之外,搜索和聚合中使用的脚本将针对可能与查询或聚合匹配的每个文档执行一次,这种情况下需要脚本能执行的非常快!
在此种API的脚本中,字段值能从doc-values, stored fields 或_source
字段访问。
_source访问字段值比doc-values慢得多,原因在于:它是针对每个结果返回多个字段进行了优化,而doc-values则针对访问许多文档中特定字段的值进行了优化。
doc['FIELD_NAME']
或doc['FIELD_NAME'].value
: _source
访问字段值:_source.field_name
_fields['field_name'].value
或 _fields['field_name']
更多API中使用脚本访问字段或特殊变量见:Painless contexts
painless作为ES中默认且最常用的脚本语言 ,这里对其做简单的介绍。
painless语法中除了作为Java语法子集的部分外,但其附加了一些其他特性,如动态类型,Map和List访问器快捷方式等。painless脚本使用ANTLR4 和 ASM 库解析和编译,编译生成的字节码直接在JVM中运行。
详细语法见: Painless Language Specification
一个Painless脚本在一个上下文中被求值。每个上下文对应于一个或多个能使用脚本的ES API,都有相应的可用作局部变量的值,详见:Painless contexts
Painless对每个上下文的方法和类都有严格的白名单,以确保所有Painless脚本都是安全的。大部分方法都是可以共享使用的,但有少部分只能在特定的ES API中使用,详见:Painless API Reference
1、reindex 时更新字段值
POST _reindex
{
"source": {
"index": "blogs"
},
"dest": {
"index": "blogs_new"
},
"script": {
"source": """
ctx._source.number_of_views += 10;
ctx._source.reindexBatch = 1;
"""
}
}
2、在update by query API中使用脚本:对row<=3且sold为false的,将cost字段值减2
POST /seats/_update_by_query
{
"query": {
"bool": {
"filter": [
{
"range": {
"row": {
"lte": 3
}
}
},
{
"match": {
"sold": false
}
}]
}
},
"script": {
"source": "ctx._source.cost -= params.discount",
"lang": "painless",
"params": {
"discount": 2
}
}
}
3、在ingest pipeline中使用script processor:如果字段locales
值为空,则其设置成"en-en"
PUT _ingest/pipeline/fix_locales
{
"processors": [
{
"script": {
"source": """
if (ctx.locales == "") {
ctx.locales = "en-en";
}
"""
}
}
]
}
值得注意的是,在 ingest processor上下文 中,通过ctx变量访问文档字段是不需要加
_source
的。
关于更多例子,读者可以参考各个Painless contexts中的例子。
[Elasticsearch Reference 7.2 » Scripting: https://www.elastic.co/guide/en/elasticsearch/reference/7.2/modules-scripting.html
Painless contexts: https://www.elastic.co/guide/en/elasticsearch/painless/7.2/painless-contexts.html
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。