Elasticsearch(ES)中的脚本(Script)

文章目录

  • [一. 脚本是什么?](#一. 脚本是什么?)
    • [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. 脚本查询(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)

虽然不是字段,但脚本的执行上下文决定了其行为,例如:

  • 查询上下文:在 querybool 查询中过滤或评分。
  • 聚合上下文:在 aggs 中生成计算字段。
  • 更新上下文:在 updateupdate_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)

  1. 存储脚本:

    json 复制代码
    POST _scripts/calculate_profit
    {
      "script": {
        "lang": "painless",
        "source": "doc['revenue'].value - doc['cost'].value"
      }
    }
  2. 调用存储脚本:

    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 指定排序值的类型(如 numberstring)。

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 传递动态参数。

关键点总结

  1. 语法规范:

    • 使用 doc['field'] 访问数值型字段(高效)。
    • 使用 ctx._source 访问文档原始内容(灵活但较慢)。
  2. 参数化:

    • 通过 params 传递动态值,避免脚本注入风险。
  3. 存储脚本:

    • 频繁使用的脚本应存储(POST _scripts/<id>)以提升性能。
  4. 安全限制:

    • 默认启用 Painless 沙箱,禁止文件/网络操作。

三. 为什么很多操作可以用脚本完成?

1 灵活性

  • 动态逻辑:无需提前定义字段或索引结构,直接通过脚本实现复杂计算。
  • 条件处理:根据实时参数或文档内容动态调整行为(如 if-else 判断)。

2 避免冗余存储

  • 按需计算:无需预先存储所有可能的派生字段(如利润、折扣价),在查询时通过脚本实时计算。

3 批量操作效率

  • 原子性更新:通过脚本直接修改文档字段,避免先获取再更新的网络开销(如 update_by_query)。

4 扩展性

  • 自定义评分:在搜索时通过脚本影响文档的相关性得分(如结合地理位置、用户行为)。

四. 脚本类型与执行方式

1 脚本语言

  • Painless(默认,安全且高性能)
  • Expression(简单数学表达式)
  • 其他(如 Groovy、JavaScript,但需谨慎启用)

2 执行上下文

  • 查询时脚本:在 queryaggs 中实时计算。
  • 索引时脚本:在文档写入时通过 ingest pipeline 处理数据。
  • 更新时脚本:在 updateupdate_by_query 中修改文档。

3 脚本存储方式

  • 内联脚本:直接嵌入到请求中(简单但重复使用时效率低)。

  • 存储脚本:将脚本保存在 ES 中,通过 ID 调用(复用性强,提升性能):

    json 复制代码
    POST _scripts/calculate_profit
    {
      "script": {
        "lang": "painless",
        "source": "doc['revenue'].value - doc['cost'].value"
      }
    }

五. 安全与性能注意事项

1 安全性

  • ES 默认启用脚本沙箱机制,限制敏感操作(如文件读写、网络访问)。

  • 可通过 elasticsearch.yml 配置禁用或限制脚本类型:

    yaml 复制代码
    script.allowed_types: inline
    script.allowed_contexts: search, update

2 性能优化

  • 避免复杂计算:脚本在查询时逐文档执行,复杂逻辑可能导致延迟。
  • 使用存储脚本:预编译存储的脚本减少解析开销。
  • 限制字段访问:通过 doc['field'](快速)而非 _source(慢)获取字段值。

六. 典型应用场景

  • 电商搜索:根据用户位置动态调整运费或显示本地化价格。
  • 日志分析:实时解析日志字段并生成统计信息。
  • 风控系统:根据用户行为实时计算风险评分。
  • 数据清洗:在索引前标准化或丰富数据(如拆分字段、添加时间戳)。
相关推荐
哲讯智能科技6 小时前
苏州SAP代理商:哲讯科技助力企业数字化转型
大数据·运维·人工智能
Edingbrugh.南空6 小时前
Apache Iceberg与Hive集成:分区表篇
大数据·hive·hadoop
武子康6 小时前
大数据-13-Hive 启动Hive DDL DML 增删改查 操作Hive的HQL
大数据·后端
刘若水7 小时前
项目 : 基于正倒排的boost搜索引擎
搜索引擎
Cachel wood8 小时前
后端开发:计算机网络、数据库常识
android·大数据·数据库·数据仓库·sql·计算机网络·mysql
村头的猫8 小时前
如何通过 noindex 阻止网页被搜索引擎编入索引?
前端·经验分享·笔记·搜索引擎
得物技术8 小时前
得物社区活动:组件化的演进与实践
java·大数据·前端
Elastic 中国社区官方博客9 小时前
使用 Azure LLM Functions 与 Elasticsearch 构建更智能的查询体验
大数据·人工智能·elasticsearch·microsoft·搜索引擎·全文检索·azure
刘天远10 小时前
深度解析企业风控API技术实践:构建全方位企业风险画像系统
大数据·数据库·数据分析
后院那片海10 小时前
GFS分布式文件系统
大数据·服务器·数据库