文章目录
- [一. 脚本是什么?](#一. 脚本是什么?)
-
- [1. `lang`(脚本语言)](#1.
lang
(脚本语言)) - [2. `source`(脚本代码)](#2.
source
(脚本代码)) - [3. `params`(参数)](#3.
params
(参数)) - [4. `id`(存储脚本的标识符)](#4.
id
(存储脚本的标识符)) - [5. `stored`(是否为存储脚本)](#5.
stored
(是否为存储脚本)) - [6. `script` 的上下文(Context)](#6.
script
的上下文(Context)) - 7.完整示例
-
- [内联脚本(Inline Script)](#内联脚本(Inline Script))
- [存储脚本(Stored Script)](#存储脚本(Stored Script))
- 8.字段总结表
- [1. `lang`(脚本语言)](#1.
- [二. 脚本能做什么?](#二. 脚本能做什么?)
-
- [1. 脚本查询(Script Query)](#1. 脚本查询(Script Query))
- [2. 脚本聚合(Script Aggregation)](#2. 脚本聚合(Script Aggregation))
- [3. 更新文档(Update By Script)](#3. 更新文档(Update By Script))
- [4. 脚本排序(Script Sort)](#4. 脚本排序(Script Sort))
- [5. 脚本字段(Script Field)](#5. 脚本字段(Script Field))
- [6. 索引时脚本(Ingest Pipeline)](#6. 索引时脚本(Ingest Pipeline))
- [7. 脚本评分(Script Score Query)](#7. 脚本评分(Script Score Query))
- [8. 数组操作(修改数组字段)](#8. 数组操作(修改数组字段))
- 关键点总结
- [三. 为什么很多操作可以用脚本完成?](#三. 为什么很多操作可以用脚本完成?)
-
- [1 灵活性](#1 灵活性)
- [2 避免冗余存储](#2 避免冗余存储)
- [3 批量操作效率](#3 批量操作效率)
- [4 扩展性](#4 扩展性)
- [四. 脚本类型与执行方式](#四. 脚本类型与执行方式)
-
- [1 脚本语言](#1 脚本语言)
- [2 执行上下文](#2 执行上下文)
- [3 脚本存储方式](#3 脚本存储方式)
- [五. 安全与性能注意事项](#五. 安全与性能注意事项)
-
- [1 安全性](#1 安全性)
- [2 性能优化](#2 性能优化)
- [六. 典型应用场景](#六. 典型应用场景)
一. 脚本是什么?
脚本是 ES 中一段可执行的代码片段,通常用于在查询或数据处理过程中动态计算值、修改文档、实现复杂逻辑。ES 支持多种脚本语言,但默认推荐使用 Painless(ES 专门为性能和安全性设计的脚本语言)。
在 Elasticsearch 中,脚本(Script)的组成通常包括以下几个核心字段,每个字段的作用和含义如下:
1. lang
(脚本语言)
-
含义:指定脚本使用的编程语言。
-
默认值:
painless
(Elasticsearch 推荐的高性能脚本语言)。 -
其他选项:
expression
(简单表达式)、groovy
(旧版本支持,需谨慎启用)。 -
示例:
json"script": { "lang": "painless", "source": "..." }
2. source
(脚本代码)
-
含义:脚本的具体逻辑代码,用指定的
lang
语言编写。 -
作用:定义动态计算逻辑,例如字段操作、条件判断、数学运算等。
-
示例:
json"script": { "source": "doc['price'].value * params.discount" }
3. params
(参数)
-
含义:传递给脚本的外部参数,用于动态调整脚本行为。
-
作用:避免硬编码,提高脚本复用性。
-
示例:
json"script": { "source": "doc['price'].value * params.discount", "params": { "discount": 0.8 } }
4. id
(存储脚本的标识符)
-
含义:引用预先存储在 Elasticsearch 中的脚本(通过
_scripts
API 存储)。 -
作用:避免重复编写相同脚本,提升性能(预编译)。
-
示例:
json"script": { "id": "calculate_profit" }
5. stored
(是否为存储脚本)
-
含义:标识脚本是否已存储(通常与
id
配合使用)。 -
示例:
json"script": { "stored": true, "id": "my_script" }
6. script
的上下文(Context)
虽然不是字段,但脚本的执行上下文决定了其行为,例如:
- 查询上下文:在
query
或bool
查询中过滤或评分。 - 聚合上下文:在
aggs
中生成计算字段。 - 更新上下文:在
update
或update_by_query
中修改文档字段。
7.完整示例
内联脚本(Inline Script)
json
{
"query": {
"script": {
"script": {
"lang": "painless",
"source": "doc['price'].value * params.discount > 100",
"params": { "discount": 0.8 }
}
}
}
}
- 字段解释:
lang
: 使用 Painless 语言。source
: 计算price
字段乘以折扣后是否大于 100。params
: 传递折扣参数0.8
。
存储脚本(Stored Script)
-
存储脚本:
jsonPOST _scripts/calculate_profit { "script": { "lang": "painless", "source": "doc['revenue'].value - doc['cost'].value" } }
-
调用存储脚本:
json{ "aggs": { "total_profit": { "sum": { "script": { "id": "calculate_profit" } } } } }
8.字段总结表
字段 | 含义 | 类型 | 是否必需 | 默认值 |
---|---|---|---|---|
lang |
脚本语言 | String | 否 | painless |
source |
脚本代码逻辑 | String | 是(或 id ) |
- |
params |
传递给脚本的参数 | Object | 否 | {} |
id |
存储脚本的唯一标识符 | String | 否 | - |
stored |
是否引用存储脚本 | Boolean | 否 | false |
二. 脚本能做什么?
脚本几乎可以覆盖 ES 的所有核心操作,常见用途包括:
以下是一些 Elasticsearch 脚本的常见使用场景示例及其详细说明:
1. 脚本查询(Script Query)
场景:根据动态条件过滤文档(如价格乘以折扣后大于 100)。
json
{
"query": {
"bool": {
"must": {
"script": {
"script": {
"lang": "painless",
"source": "doc['price'].value * params.discount > params.threshold",
"params": {
"discount": 0.8,
"threshold": 100
}
}
}
}
}
}
}
说明:
- 使用
params
传递折扣率和阈值,避免硬编码。 doc['price']
直接访问字段的数值类型(比_source
更高效)。
2. 脚本聚合(Script Aggregation)
场景:按利润(收入 - 成本)分组统计。
json
{
"aggs": {
"profit_groups": {
"terms": {
"script": {
"lang": "painless",
"source": "doc['revenue'].value - doc['cost'].value"
},
"size": 10
}
}
}
}
说明:
- 通过脚本动态计算利润字段,无需预先存储该字段。
- 使用
terms
聚合对利润分桶统计。
3. 更新文档(Update By Script)
场景:为符合条件的文档增加浏览量(views
字段 +1)。
json
POST /index/_update_by_query
{
"script": {
"source": "ctx._source.views += params.increment",
"params": { "increment": 1 }
},
"query": { "term": { "user": "alice" } }
}
说明:
ctx._source
访问文档的原始内容。- 通过
params.increment
参数化增量值,避免硬编码。
4. 脚本排序(Script Sort)
场景:根据动态权重(如点击量乘以系数)排序。
json
{
"sort": {
"_script": {
"type": "number",
"script": {
"source": "doc['clicks'].value * params.weight",
"params": { "weight": 1.5 }
},
"order": "desc"
}
}
}
说明:
- 使用
_script
自定义排序逻辑。 type
指定排序值的类型(如number
或string
)。
5. 脚本字段(Script Field)
场景:在查询结果中添加一个动态计算的字段(如价格等级)。
json
{
"query": { "match_all": {} },
"script_fields": {
"price_level": {
"script": {
"source": """
if (doc['price'].value > 1000) {
return 'high';
} else {
return 'low';
}
"""
}
}
}
}
说明:
script_fields
在返回结果中添加一个临时字段price_level
。- 使用条件判断(
if-else
)动态分类。
6. 索引时脚本(Ingest Pipeline)
场景:在数据写入时自动添加时间戳字段。
json
PUT _ingest/pipeline/add_timestamp
{
"description": "Add timestamp at ingest time",
"processors": [
{
"script": {
"source": "ctx.timestamp = new Date().getTime()"
}
}
]
}
说明:
- 通过 Ingest Pipeline 在索引时执行脚本。
ctx
表示当前文档的上下文,直接修改字段。
7. 脚本评分(Script Score Query)
场景:根据自定义逻辑影响文档相关性评分(如按点击量加分)。
json
{
"query": {
"script_score": {
"query": { "match_all": {} },
"script": {
"source": "Math.log(1 + doc['clicks'].value) * params.boost",
"params": { "boost": 2 }
}
}
}
}
说明:
script_score
结合数学函数(如对数)动态调整评分。params.boost
控制权重参数。
8. 数组操作(修改数组字段)
场景:向文档的 tags
数组中添加新标签。
json
POST /index/_update/1
{
"script": {
"source": "ctx._source.tags.add(params.new_tag)",
"params": { "new_tag": "popular" }
}
}
说明:
ctx._source.tags.add()
直接操作数组字段。- 使用
params
传递动态参数。
关键点总结
-
语法规范:
- 使用
doc['field']
访问数值型字段(高效)。 - 使用
ctx._source
访问文档原始内容(灵活但较慢)。
- 使用
-
参数化:
- 通过
params
传递动态值,避免脚本注入风险。
- 通过
-
存储脚本:
- 频繁使用的脚本应存储(
POST _scripts/<id>
)以提升性能。
- 频繁使用的脚本应存储(
-
安全限制:
- 默认启用 Painless 沙箱,禁止文件/网络操作。
三. 为什么很多操作可以用脚本完成?
1 灵活性
- 动态逻辑:无需提前定义字段或索引结构,直接通过脚本实现复杂计算。
- 条件处理:根据实时参数或文档内容动态调整行为(如
if-else
判断)。
2 避免冗余存储
- 按需计算:无需预先存储所有可能的派生字段(如利润、折扣价),在查询时通过脚本实时计算。
3 批量操作效率
- 原子性更新:通过脚本直接修改文档字段,避免先获取再更新的网络开销(如
update_by_query
)。
4 扩展性
- 自定义评分:在搜索时通过脚本影响文档的相关性得分(如结合地理位置、用户行为)。
四. 脚本类型与执行方式
1 脚本语言
- Painless(默认,安全且高性能)
- Expression(简单数学表达式)
- 其他(如 Groovy、JavaScript,但需谨慎启用)
2 执行上下文
- 查询时脚本:在
query
或aggs
中实时计算。 - 索引时脚本:在文档写入时通过
ingest pipeline
处理数据。 - 更新时脚本:在
update
或update_by_query
中修改文档。
3 脚本存储方式
-
内联脚本:直接嵌入到请求中(简单但重复使用时效率低)。
-
存储脚本:将脚本保存在 ES 中,通过 ID 调用(复用性强,提升性能):
jsonPOST _scripts/calculate_profit { "script": { "lang": "painless", "source": "doc['revenue'].value - doc['cost'].value" } }
五. 安全与性能注意事项
1 安全性
-
ES 默认启用脚本沙箱机制,限制敏感操作(如文件读写、网络访问)。
-
可通过
elasticsearch.yml
配置禁用或限制脚本类型:yamlscript.allowed_types: inline script.allowed_contexts: search, update
2 性能优化
- 避免复杂计算:脚本在查询时逐文档执行,复杂逻辑可能导致延迟。
- 使用存储脚本:预编译存储的脚本减少解析开销。
- 限制字段访问:通过
doc['field']
(快速)而非_source
(慢)获取字段值。
六. 典型应用场景
- 电商搜索:根据用户位置动态调整运费或显示本地化价格。
- 日志分析:实时解析日志字段并生成统计信息。
- 风控系统:根据用户行为实时计算风险评分。
- 数据清洗:在索引前标准化或丰富数据(如拆分字段、添加时间戳)。