脚本字段与脚本查询
script_fields 在返回结果时用 painless 脚本计算派生字段,结果不写入索引;script query 则把脚本当作过滤条件,只有脚本判断为真的文档才被返回。两者都依赖字段的正排存储(doc values)或原始 _source 取值,因此脚本里能取到哪些字段取决于映射配置。
为什么要注意编译缓存
painless 脚本的编译结果会被缓存,但缓存与脚本源码相关:把常量直接写死在 source 里,每个不同取值都会触发一次重新编译;把变量放进 params 传递,才能复用同一份编译产物。这是脚本类查询里最容易踩、也最容易修的性能细节。
基本信息
| script_fields | 返回时计算派生字段,如 doc['price'].value * 1.1 |
|---|---|
| script query | 用脚本作为过滤条件,命中才返回 |
| 取值方式 | doc[...] 走正排 doc values,params._source 走原始 JSON |
| 性能特征 | 逐文档执行,无法走索引,过滤大结果集时开销明显 |
| 适用版本 | painless 自 5.0 起为默认脚本语言 |
示例
示例
GET /my-index/_search
{
"query": { "match_all": {} },
"script_fields": {
"price_with_tax": {
"script": { "lang": "painless", "source": "doc['price'].value * params.rate", "params": { "rate": 1.1 } }
}
}
}
# 脚本查询:满足条件才返回
GET /my-index/_search
{ "query": { "script": { "script": { "source": "doc['num'].value > params.min", "params": { "min": 1 } } } } }
# 访问可能缺失的字段前先判断,避免抛异常
# source: "doc.containsKey('num') && doc['num'].value > 1"典型使用场景
- 派生展示:含税价、折后金额等只在返回时计算,不占用索引空间
- 复杂过滤:需要跨字段运算或自定义判断时用脚本查询
- 临时探索:一次性分析不想为此新增字段
- 排序策略:按脚本算出的分数排序,注意只在小结果集上使用
- 口径核对:用脚本输出中间值定位字段计算差异
写法与代价
| 写法 | 含义 | 代价 |
|---|---|---|
| script_fields | 返回时计算,不落索引 | 逐命中计算,命中多时耗时线性增长 |
| script query | 作为过滤条件 | 无法用索引,逐文档判断 |
| doc['f'].value | 取正排序值 | text 字段没有正排,需改取 keyword |
| params._source.f | 读取原始 JSON | 速度更慢,频繁使用要评估 |
脚本里直接访问缺失字段会抛异常,取值前用 doc.containsKey 判断更稳;对 text 字段用 doc 取值需要开启 fielddata,正确做法是改取 keyword 子字段。
脚本查询绕过倒排索引、逐文档执行,在大数据集上容易变成集群热点;能用普通查询表达的逻辑不要用脚本实现。