Elasticsearch脚本使用

如何使用Elasticsearch脚本

Elasticsearch默认脚本语言为Painless。其他lang插件使您可以运行以其他语言编写的脚本。

语言 沙盒 是否需要插件 备注
painless 内置的 默认脚本语言
expression 内置的 快速的自定义排名和排序
mustache 内置的 范本

脚本语法

"script": {
    "lang":   "...",  
    "source" | "id": "...", 
    "params": { ... } 
}

参数解释

参数名称 作用
lang 脚本使用的语言
source 内联脚本,或者id存储脚本指定脚本本身
params 应该传递给脚本的任何命名参数

官方文档中提到一个情况:Elasticsearch遇到一个新脚本时,将对其进行编译并将编译后的版本存储在缓存中。编译可能是一个繁重的过程。如果我们有相同逻辑的脚本只是参数不一样的时候,应以命名形式传递变量,而不是将值硬编码到脚本本身中。

第一个脚本每一次乘数的改变都必须重新编译。

 "source": "doc['my_field'] * 2"

第二个脚本参数发生改变的时候,只有在第一次遇见的时候进行编译。

 "source": "doc['my_field'] * multiplier",
  "params": {
    "multiplier": 2
  }

简单脚本

当脚本中source只有很简单的内容,不涉及参数变化等复杂逻辑可以直接在script后使用字符串而不是对象。

下面两个脚本是等价的:

 “ script” :“ ctx._source.likes ++
"script": {
    "source": "ctx._source.likes++"
}

保存脚本

我们可以将脚本保存在集群中,然后使用它的时候只需要指定其ID。

脚本保存

保存脚本需要使用/_scripts/{id}POST请求

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

查询脚本

查询脚本需要使用/_scripts/{id}GET请求

GET _scripts/calculate-score

使用保存的脚本

后续再使用脚本的时候就可以指定ID,而不是再次编写脚本了。

GET _search
{
  "query": {
    "script": {
      "script": {
        "id": "calculate-score",
        "params": {
          "my_modifier": 2
        }
      }
    }
  }
}

删除脚本

DELETE _scripts/calculate-score

默认情况下,脚本没有基于时间的到期,但是可以通过script.cache.expire设置来更改此行为。也可以使用该script.cache.max_size设置来配置此缓存的大小。默认情况下,缓存大小为100。

脚本中访问文档字段和特殊变量

根据脚本使用的不同位置,它们可以访问的变量的文档字段是不同的。

更新操作中脚本访问数据

updateupdate-by-queryreindex API中使用的脚本将有权访问显示以下内容的ctx变量

参数 作用
ctx._source 访问文档_source字段
ctx.op 应用于文档的操作:index或delete
ctx._index 访问文档元字段,其中某些字段可能是只读的

在搜索和聚合中脚本访问数据

进行搜索和聚合操作中的脚本,除了会对命中的数据进行脚本字段操作之外,针对可能与查询或汇总匹配的文档都会执行一次。这使得搜索中脚本操作的文档量会根据实际拥有的文档数量,对于比较大的业务数据会非常庞大,所以要求脚本必须足够的效率。

官方建议使用doc-valuesstored fields段或_source field从脚本访问字段值 ,

官方提供的三种访问方式

访问语法 使用场景 说明
doc[‘field_name’] 需要注意的是doc值只能返回“简单”的字段值,如数字、日期、地理点、术语等(numeric, date, or geopoint 目前从脚本中获取字段值最快的方式
_fields[‘field_name’].value或_fields[‘field_name’] 字段必须被设置为stored
params._source.field_name 普通的访问方式

创建例子

现在创建一个测试用的索引

PUT test_index/_mapping
{
    "properties": {
        "name": {
            "type": "text",
            "store": true
        },
        "desc": {
            "type": "text"
        },
        "num":  {
          "type":"long"
        }
    }
}

PUT test_index/_doc/2?refresh
{
  "name":"测试",
  "desc":"描述",
  "num" : 100
}

doc-values

目前从脚本中获取字段值最快的方式是使用doc['field_name']格式,此方法会从doc值中检索字段数据,但是需要注意的是需要注意的是doc值只能返回“简单”的字段值,如数字、日期、地理点、术语等,如果字段是多值的,则只能返回这些值的数组。它不能返回JSON对象。

在对上面的索引执行脚本test的时候会成功完成, 但是执行脚本test2的时候会发生错误

GET test_index/_search
{
  "script_fields": {
    "test": {
      "script": {
        "lang":   "expression",
        "source": "doc['num']"
      }
    }
  }
}

GET test_index/_search
{
  "script_fields": {
    "test2": {
      "script": {
        "lang":   "expression",
        "source": "doc['name']"
      }
    }
  }
}

执行脚本test2会发生错误

"reason": "Fielddata is disabled on text fields by default.
 Set fielddata=true on [name] in order to load fielddata in memory by uninverting the inverted index. 
 Note that this can however use significant memory.
 Alternatively use a keyword field instead."

官方对此说明是

如果启用了fielddata, doc[‘field’]语法也可以用于分析的文本字段,但是要注意:在文本字段上启用fielddata需要将所有的术语加载到JVM堆中,这在内存和CPU方面都非常昂贵。从脚本访问文本字段很少有意义。

params._fields[‘field_name’]

使用params._fields['field_name']可以访问被设置为"store": true的参数。但是对没有设置此参数的数据使用此方法时会抛出异常

GET test_index/_search
{
  "script_fields": {
    "test": {
      "script": {
        "lang":   "painless",
        "source": "params._fields['desc']"
      }
    }
  }
}

上面请求的desc没有被设置"store": true此时会返回内容

"error": {
    "root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "cannot write xcontent for unknown value of type class org.elasticsearch.search.lookup.FieldLookup"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "cannot write xcontent for unknown value of type class org.elasticsearch.search.lookup.FieldLookup"
  }

params._source.field_name

此方式对数据的访问没有其他限制

三种访问方式对比

  1. 存储字段取值方式比doc取值慢得多。doc值针对访问许多文档中特定字段的值进行了优化,使用_source或存储字段取值它们针对每个结果返回多个字段进行了优化。对于需要获取结果中内容生成脚本字段时是有意义的。但是对于一些聚合或者排序操作的时候,doc取值会更好。

  2. _source和存储字段取值。_source字段只是一个特殊的存储字段,因此其性能与其他存储字段类似。使用存储字段取值而不是_source字段取值可能的场景是在当在_source非常大的时候访问几个小的存储字段而不是整个_source的成本更低。

脚本安全性的配置

默认情况下,允许执行所有脚本类型。通过修改script.allowed_types我们可以指定允许执行的类型;

## 只允许执行内联脚本,而不允许执行存储脚本(或任何其他类型)。
script.allowed_types: inline 

默认情况下,允许执行所有脚本上下文。通过修改cript.allowed_contexts只允许一部分操作执行

## 这将只允许执行搜索和更新脚本,而不允许执行aggs或插件脚本(或任何其他上下文)
script.allowed_contexts: search, update 

个人水平有限,上面的内容可能存在没有描述清楚或者错误的地方,假如开发同学发现了,请及时告知,我会第一时间修改相关内容。假如我的这篇内容对你有任何帮助的话,麻烦给我点一个赞。你的点赞就是我前进的动力。

发布了204 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq330983778/article/details/103539418