脚本
1.什么是 Elasticsearch 脚本
Elasticsearch 脚本(Script
)是一种在 查询、聚合 或 更新文档 时执行自定义逻辑的方式。它允许用户在查询时动态计算值、修改文档或实现复杂的业务逻辑,而无需预先处理数据。
2.为什么需要有脚本
脚本在 Elasticsearch 中有几个重要用途:
- 动态计算:在 查询时 计算字段值,而不是 索引时。
- 复杂逻辑:实现简单的
if-else
、循环等编程结构。 - 字段转换:将存储的字段转换为查询所需的格式。
- 自定义评分:实现更复杂的相关性评分算法。
- 批量更新:基于条件或计算更新多个文档。
3.常用的脚本类型
Elasticsearch 支持多种脚本语言,最常用的包括:
Painless
:Elasticsearch 默认的安全脚本语言(推荐使用)。Expression
:简单的表达式语言,性能较好但功能有限。Mustache
:主要用于模板。Java
:直接使用 Java 代码(高权限,不推荐)。
4.Painless 脚本语言
Painless 是 Elasticsearch 专门设计的脚本语言,具有以下特点:
- 安全:限制了可能有害的操作。
- 高性能:编译执行,接近原生性能。
- 简单易用:语法类似 Java / JavaScript。
- 强类型:支持类型检查。
- 内置 Elasticsearch API:可以直接访问文档字段和特殊变量。
5.实际案例
5.1 创建索引并插入产品数据
创建 products
索引。
PUT /products
{
"mappings": {
"properties": {
"name": { "type": "text" },
"category": { "type": "keyword" },
"price": { "type": "double" },
"quantity": { "type": "integer" },
"is_premium": { "type": "boolean" }
}
}
}
插入测试数据。
POST /products/_bulk
{"index":{}}
{"name":"4K Smart TV","category":"electronics","price":899.99,"quantity":50,"is_premium":true}
{"index":{}}
{"name":"Wireless Headphones","category":"electronics","price":199.99,"quantity":120,"is_premium":false}
{"index":{}}
{"name":"Laptop","category":"electronics","price":1299.99,"quantity":30,"is_premium":true}
{"index":{}}
{"name":"Coffee Maker","category":"home","price":79.99,"quantity":200,"is_premium":false}
{"index":{}}
{"name":"Blender","category":"home","price":49.99,"quantity":150,"is_premium":false}
{"index":{}}
{"name":"Smartphone","category":"electronics","price":699.99,"quantity":80,"is_premium":true}
{"index":{}}
{"name":"Desk Chair","category":"office","price":149.99,"quantity":75,"is_premium":false}
{"index":{}}
{"name":"Monitor","category":"electronics","price":249.99,"quantity":60,"is_premium":false}
查询验证。
GET /products/_count
GET /products/_search
{
"query": { "match_all": {} },
"size": 5
}
5.2 创建索引并插入销售数据
创建 sales
索引。
PUT /sales
{
"mappings": {
"properties": {
"product_id": { "type": "keyword" },
"product_name": { "type": "text" },
"quantity": { "type": "integer" },
"unit_price": { "type": "double" },
"is_premium": { "type": "boolean" },
"sale_date": { "type": "date" }
}
}
}
插入测试数据。
POST /sales/_bulk
{"index":{}}
{"product_id":"P1001","product_name":"4K Smart TV","quantity":2,"unit_price":899.99,"is_premium":true,"sale_date":"2023-01-15"}
{"index":{}}
{"product_id":"P1002","product_name":"Wireless Headphones","quantity":5,"unit_price":199.99,"is_premium":false,"sale_date":"2023-01-16"}
{"index":{}}
{"product_id":"P1003","product_name":"Laptop","quantity":1,"unit_price":1299.99,"is_premium":true,"sale_date":"2023-01-17"}
{"index":{}}
{"product_id":"P1004","product_name":"Coffee Maker","quantity":3,"unit_price":79.99,"is_premium":false,"sale_date":"2023-01-18"}
{"index":{}}
{"product_id":"P1005","product_name":"Blender","quantity":2,"unit_price":49.99,"is_premium":false,"sale_date":"2023-01-19"}
{"index":{}}
{"product_id":"P1006","product_name":"Smartphone","quantity":4,"unit_price":699.99,"is_premium":true,"sale_date":"2023-01-20"}
{"index":{}}
{"product_id":"P1007","product_name":"Desk Chair","quantity":1,"unit_price":149.99,"is_premium":false,"sale_date":"2023-01-21"}
{"index":{}}
{"product_id":"P1008","product_name":"Monitor","quantity":2,"unit_price":249.99,"is_premium":false,"sale_date":"2023-01-22"}
查询验证。
GET /sales/_count
GET /sales/_search
{
"query": { "match_all": {} },
"size": 5
}
5.3 执行脚本
5.3.1 通过脚本:自定义字段(Script Fields)
GET /products/_search
{
"query": { "match_all": {} },
"script_fields": {
"discounted_price": {
"script": {
"source": "doc['price'].value * 0.9"
}
}
}
}
5.3.2 通过脚本:排序(Script Sort)
GET /products/_search
{
"query": { "match_all": {} },
"sort": {
"_script": {
"type": "number",
"script": {
"source": "doc['price'].value * params.discount",
"params": {
"discount": 0.8
}
},
"order": "asc"
}
}
}
5.3.3 通过脚本:更新字段(Update by Query)
POST /products/_update_by_query
{
"script": {
"source": "ctx._source.price *= params.discount",
"params": {
"discount": 0.9
}
},
"query": {
"range": {
"price": { "gte": 100 }
}
}
}
只更新价格 ≥100
的商品。
gte
操作符:表示greater than or equal to
(≥
)。- 其他常用操作符:
gt
:大于(>
)lte
:小于或等于(≤
)lt
:小于(<
)
5.3.4 条件查询(Conditional Script)
GET /products/_search
{
"query": {
"bool": {
"filter": {
"script": {
"script": {
"source": """
def category = doc['category'].value;
def price = doc['price'].value;
return category == 'electronics' && price > 500 && price < 1000;
"""
}
}
}
}
}
}
5.3.5 聚合
计算销售记录的总金额,并根据是否为 Premium(高级/优质) 商品进行额外加成。
GET /sales/_search
{
"aggs": {
"total_sales": {
"sum": {
"script": {
"source": """
def total = doc['quantity'].value * doc['unit_price'].value;
if (doc['is_premium'].value) {
total *= 1.1; // 10% premium
}
return total;
"""
}
}
}
}
}
Painless 脚本为 Elasticsearch 提供了强大的灵活性,使得在 不重新索引数据 的情况下实现复杂业务逻辑成为可能。