第五阶段 44 · Painless 脚本与 runtime fields(运行时字段)
2026/8/4 2:21:39 网站建设 项目流程

44 · Painless 脚本与 runtime fields(运行时字段)

阶段:第五阶段 / 进阶
ES:script fields / script query / update script / runtime fields | PostgreSQL:表达式列 / 生成列 /UPDATE SET col = expr


1. 概念

Painless是 ES 内置脚本语言(类 Java)。四个高频用途:

用途作用SQL 类比
script field查询时算一个临时字段返回SELECT price*qty AS amount
script query用脚本做过滤条件WHERE price*qty > 1000
update script按脚本原地改文档UPDATE SET n = n + 1
runtime fieldmapping 里定义查询时计算的字段(不占存储)生成列(虚拟)

原则:脚本灵活但慢,能用普通字段/查询解决就别上脚本。


2. PostgreSQL 对照

-- 表达式列SELECTprice*qtyASamountFROMsales;-- 生成列(存储或虚拟)≈ runtime fieldALTERTABLEsalesADDCOLUMNamountnumericGENERATED ALWAYSAS(price*qty)STORED;-- 脚本更新UPDATEsalesSETview_count=view_count+1WHEREid=1;

3. ES DSL

3.1 script field(查询时算字段)

GET sales_idx/_search { "script_fields": { "amount": { "script": { "source": "doc['price'].value * doc['qty'].value" } } } }

3.2 script query(脚本过滤)

GET sales_idx/_search { "query": { "bool": { "filter": { "script": { "script": "doc['price'].value * doc['qty'].value > params.min", "params": { "min": 1000 } } } } } }

3.3 update by script(自增)

POST sales_idx/_update/1 { "script": { "source": "ctx._source.view_count += params.n", "params": { "n": 1 } } }

3.4 runtime field(查询时计算的虚拟字段,推荐)

PUT sales_idx/_mapping { "runtime": { "amount": { "type": "double", "script": { "source": "emit(doc['price'].value * doc['qty'].value)" } } } } GET sales_idx/_search { "query": { "range": { "amount": { "gte": 1000 } } } } // 像普通字段一样用

4. Spring Boot 实现

@ComponentpublicclassDoc44Script{@AutowiredprivateElasticsearchClientelasticsearchClient;/** 用 params 传参的脚本过滤:price*qty > min */publicList<Map<String,Object>>byAmountScript(StringindexName,doublemin)throwsIOException{SearchResponse<Map>resp=elasticsearchClient.search(s->s.index(indexName).query(q->q.bool(b->b.filter(f->f.script(sc->sc.script(scr->scr.source("doc['price'].value * doc['qty'].value > params.min").params("min",JsonData.of(min))))))),Map.class);returnresp.hits().hits().stream().map(Hit::source).filter(Objects::nonNull).collect(Collectors.toList());}}

import:co.elastic.clients.json.JsonData动态值一律走params,别拼进source
字符串——既能被脚本缓存复用,又避免注入。


5. 坑与最佳实践

  1. 能不用脚本就不用:脚本不走倒排、逐文档执行,慢且难缓存。
  2. 一定用params传参:相同source+ 不同params才会命中脚本编译缓存。
  3. doc['field']vsparams._sourcedoc[...]读 doc_values(快,text不可用);
    ctx._source/params._source读原文(慢)。过滤/排序用doc[...]
  4. runtime field 换取灵活性:查询时算、零存储、可随时改定义;代价是每次查询都算,重查询慎用。
  5. update script 注意并发:高并发自增用_update+ 乐观锁(if_seq_no/if_primary_term)。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询