es运行时字段
"properties" : {
"app_id" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"archive_id" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
如上是mapping中的部分字段,然后put如下
PUT /business_order_detail_test/_mapping
{
"runtime": {
"app_archive_key": {
"type": "keyword",
"script": {
"source": "if (doc['app_id.keyword'].size() != 0 && doc['archive_id.keyword'].size() != 0) { emit(doc['app_id.keyword'].value + '|' + doc['archive_id.keyword'].value); }"
}
}
}
}
在现有索引上添加一个“运行时字段”(Runtime Field),用于在查询时动态生成一个组合键,而不修改底层索引数据或重新索引。
为了让你更清楚,我把它拆解成几个层面来解释:
- 它做了什么(技术动作)
你通过 PUT /business_order_detail_test/_mapping 更新了索引的映射。
新增了一个名为 app_archive_key 的运行时字段。
这个字段不会被物理存储在磁盘上,只在查询(Search)或聚合(Aggregation)时实时计算。
- 脚本逻辑(计算规则)
脚本的逻辑是拼接字符串:
doc[‘app_id.keyword’].size() != 0:检查文档中是否存在 app_id 且不为空值。
doc[‘archive_id.keyword’].size() != 0:检查文档中是否存在 archive_id 且不为空值。
如果两个字段都存在,则执行 emit(…),将 app_id 和 archive_id 用竖线 | 连接起来。
- 为什么要用 .keyword 而不是直接用 app_id?
你给的映射中,app_id 和 archive_id 都是 text 类型(用于全文分词搜索),但同时拥有 keyword 子字段(用于精确匹配、聚合和排序)。
脚本里用了 doc[‘app_id.keyword’].value,是因为 text 类型默认会分词(比如 “abc123” 可能被拆成 “abc” 和 “123”),直接取值会报错或取不到完整值。
使用 .keyword 可以确保取到该字段的原始完整值(如 “order_001”),保证拼接出的组合键是精确的。
- 实际业务场景(为什么要这么做)
假设你有两条数据:
文档 A:app_id=“shop”, archive_id=“2024”
文档 B:app_id=“shop”, archive_id=“2025”
查询时,app_archive_key 会动态计算出:
文档 A:“shop|2024”
文档 B:“shop|2025”
这样做的好处是:
无需重索引:如果直接写死一个拼接字段,需要修改数据源并重建索引。使用运行时字段,可以零停机成本地新增这个逻辑。
灵活聚合:你可以直接对这个 app_archive_key 进行 terms 聚合,来统计每种 (app_id, archive_id) 组合下的文档数量,或者用它来做精确过滤(term 查询)。
⚠️ 需要注意的性能代价:
因为这是查询时计算,每次搜索都会执行这个脚本。如果数据量极大或并发很高,会消耗较多的 CPU 资源。如果该组合键频繁用于聚合或过滤,建议还是考虑在写入数据时(通过 Ingest Pipeline)直接写入这个字段,或者在创建索引时就定义为普通字段并重新索引数据。
更多推荐
所有评论(0)