首页/开发/正则与编码速查/ES 脚本正则查询的实现方式

ES 脚本正则查询的实现方式

在 Elasticsearch 里做逐字符正则只能靠 script query 或 script_fields 配 Painless:Painless 内置 /regex/ 字面量、=~ 与 ==~ 操作符,底层是 Java 正则,逐文档执行且不走索引,必须靠外层过滤缩小候选集。

正则与编码速查开发更新于 2026-10-17
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌​​‌​‌‌‌‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌‌​​​​​‌​‌‌‌‌​‌‌​​‌​‌​‌‌‌​​‌‌​​‌​‌‌​‌​‌‌‌​​‌‌​‌‌​​​‌‌​‌‌‌​​‌​​‌‌​‌​​‌​‌‌‌​​​​​‌‌‌​‌​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌‌​​​​‌‌‌​​​​​​‌​‌‌​‌​‌‌‌​‌​‌​‌‌‌​​‌‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌‌​​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​

脚本正则的定位

regexp query 只能匹配词项,遇到「跨字段组合判断」「对原文逐字符校验」「需要环视与反向引用」时只能用脚本。Painless 是 Java 语法子集,可直接写正则字面量和匹配操作符,本质调用 java.util.regex。代价明确:脚本逐文档执行,不利用倒排索引,只适合候选集已经被 term、range 过滤压到很小的情况。

基本信息

适用版本Elasticsearch 6.x 起用 Painless 替换 Groovy
执行位置script query / script_fields / runtime field 等
正则接口/pattern/ 字面量、=~(查找)、==~(完整匹配)、java.util.regex.Pattern
取值方式doc['f'].value 取 keyword/数值;或 params._source['f'] 取原文
缓存特性脚本编译结果有缓存,匹配结果不进 query cache
成本对候选文档逐条求值,线性于文档数

典型使用场景

  • 原文级校验:keyword 里存了整串 JSON、URL 或 UA,要按整体形态判断(走倒排索引做不到)
  • 跨字段组合:例如 a 字段匹配某个模式且 b 字段落在某区间
  • 正则带环视或反向引用:词项级 regexp 查询不支持,只能落到脚本里的 Java 正则
  • 自定义打分或运行时字段:用命中结果算分、抽子串生成新字段
  • 一次性数据治理:导出前统计不规范值(务必限时间范围与小索引)

Painless 脚本示例

Painless 脚本示例
{
  "query": {
    "bool": {
      "filter": [ { "term": { "app": "gateway" } } ],   # 先用索引条件压小候选集
      "must": [
        {
          "script": {
            "script": {
              "lang": "painless",
              "source": "def v = doc['uri.keyword'].value; if (v == null) return false; return v ==~ /\\/api\\/v[0-9]+\\/.*/;"
            }
          }
        }
      ]
    }
  }
}
# 说明:==~ 要求整串匹配,=~ 只要求出现;两处反斜杠是 JSON 转义所致

接口与语义对照

写法语义使用注意
s =~ /re/部分匹配:串中出现即真最短语义,等价 Java Matcher.find()
s ==~ /re/完整匹配:整串必须符合等价 Java Matcher.matches(),常用于字段值校验
Pattern.compile(x)手动编译再复用每次查询都编译开销大,重复模式优先写在脚本常量或 params 外
doc['f'].value取 doc values(keyword/数值)text 字段默认无 doc values,直接取会报错
params._source['f']取 _source 原文取决于 _source 是否关闭,且解析开销更高
script_fields / runtime field把结果作为字段返回或参与查询runtime field 可用字段上下文,但计算同样逐文档发生
脚本是集群最容易被打爆的入口:正则写在脚本里会随查询反复执行,且无法被索引优化。上线前务必把候选集压到小范围、禁止直接把用户输入拼进脚本(Painless 虽为沙箱,但正则自身的回溯开销仍需自控),并用 profile 观察耗时。
版本差异:Painless 正则字面量、=~、==~ 均在较新版本可用;老版本(2.x 之前的 Groovy 脚本)写法完全不同。生产环境统一用参数化脚本(store script 或 params),可读性与复用性都更好,具体接口以官方 Painless 文档为准。
若只是「原文包含某子串」,不要用脚本:用 match_phrase 或 wildcard 更便宜;若只是固定形态匹配,用 regexp query 打 keyword 字段。脚本只留给索引层确实表达不了的条件。

易错点

  • 把脚本正则当成通用搜索手段:逐文档执行导致查询从毫秒级掉到秒级,集群负载整体抬升 → 先用 filter 压小候选集,能走索引的条件一律放外层
  • 在 script 里直接读 text 字段:text 无 doc values,报 fielddata 未启用错误 → 改用 .keyword、或读取 params._source 原文
  • 正则里的反斜杠在 JSON 与 Painless 里各转义一层:少写一个反斜杠语义就变了 → 写完后在小数据集上验证命中结果
  • 每次查询都 Pattern.compile:编译开销被反复支付 → 用参数化脚本或 runtime field 复用编译结果
  • 用脚本做全量数据治理:未限时间与索引范围,触发 circuit breaker 或超时中断 → 分批跑(按时间切片 + search_after 翻页)

数据来源与参考

  • Elasticsearch 官方文档:Painless scripting language 与 script query
  • Elasticsearch 官方文档:Scripts, caching, and search speed
  • Oracle Java 文档:java.util.regex.Pattern / Matcher

整理校对:其其网ICT文档中心 · 本页地址:docs.qiqicto.com/regex/es-script-regexp-usage.html

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌​​‌​‌‌‌‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌‌​​​​​‌​‌‌‌‌​‌‌​​‌​‌​‌‌‌​​‌‌​​‌​‌‌​‌​‌‌‌​​‌‌​‌‌​​​‌‌​‌‌‌​​‌​​‌‌​‌​​‌​‌‌‌​​​​​‌‌‌​‌​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌‌​​​​‌‌‌​​​​​​‌​‌‌​‌​‌‌‌​‌​‌​‌‌‌​​‌‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌‌​​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​