regexp query 是什么
regexp 是 ES 的 term 级查询:它不扫描文档原文,而是把正则编译成自动机,去匹配倒排索引里的词项(term)。所以表达式描述的是一个词项的形状,要按原文匹配就必须落在 keyword 字段上(text 字段要用 .keyword 子字段)。语法来自 Lucene RegExp,属于自动机实现,与 PCRE 在能力上有明确取舍。
基本信息
| 适用版本 | Elasticsearch 7.x / 8.x(语法随 Lucene 版本演进) |
|---|---|
| 查询层级 | term 级:regexp、wildcard、prefix 同属高开销一族 |
| 匹配单位 | 字段的单个词项,不是文档原文 |
| 字段要求 | keyword / term 索引;text 需 .keyword 子字段 |
| 语法来源 | Lucene RegExp(自动机),非 PCRE/Perl |
| 开销控制 | max_determinized_states 限制自动机状态数 |
典型使用场景
- 按形态筛订单号、错误码、trace_id:这类字段天然是 keyword,适合固定结构的模式匹配
- 数据质量巡检:找出不符合命名规范的用户名、sku、标签值
- 日志检索前做粗筛:先用时间范围与 term 过滤把候选集压小,再对少量词项上正则
- 需要字符类与重复次数精确控制,而 wildcard 的星号与问号表达力不够时
- 配合 terms 聚合做分类前处理,把同类词项归到规则模式上
DSL 写法示例
DSL 写法示例
{
"query": {
"regexp": {
"order_no": {
"value": "AB[0-9]{6}-[0-9]{4}", # 固定前缀 + 6 位数字 + 连字符 + 4 位数字
"case_insensitive": true, # 7.10 起支持,默认 false
"max_determinized_states": 10000, # 自动机状态上限,防内存与 CPU 抖动
"flags": "ALL" # 可选语法开关:COMPLEMENT / INTERSECTION / INTERVAL
}
}
}
}
# 验证代价:请求加 profile: true,看 regexp 阶段耗时与匹配文档数Lucene 语法支持对照
| 写法 | 含义 | 注意事项 |
|---|---|---|
| [a-z] [^a-z] | 字符类与取反 | 不支持 \d \w \s 简写,要写成 [0-9]、[A-Za-z0-9_] |
| . * + ? {n,m} | 任意字符与重复次数 | 前导通配(点星开头)退化为全词项遍历 |
| ( ) | | 分组与或 | 只用于组合,没有捕获语义,无法反向引用 |
| & ~ <> | 交集、取反、区间 | 需经 flags 显式开启对应可选语法 |
| ^ $ | 锚定 | regexp 本身已要求整词匹配,锚点通常无意义 |
| 环视与反向引用 | PCRE 常见特性 | 不支持,属于自动机语法的天然边界 |
前导通配最危险:以任意字符重复开头时 Lucene 无法做前缀剪枝,字段词项基数一大就把每个词项送进自动机,轻则超时,重则抛自动机状态超限异常并把节点 CPU 打满。生产上先用 filter 把候选集压小。
匹配语义是整词:pattern 相当于前后都加了锚定,写 foo 只命中恰好为 foo 的词项,要命中含 foo 的词项必须显式允许前后有任意字符。中文没有空格分词,text 字段里的词项常是单字或长串,用 regexp 匹配中文前先确认字段映射与分词器。
需要环视、跨字段判断或逐字符处理时,regexp 无解:改用 script query 配合 Painless 的 Java 正则,代价是逐文档执行、不走索引。两种方案的取舍见脚本正则条目。