1、背景
在咱们应用 es
的开发过程中可能会遇到这么一种状况,比方咱们的线路名称字段 lineName
字段在设置 mapping
的时候应用的是 text
类型,然而前期发现须要应用这个字段来进行 聚合操作
,那么咱们除了对索引进行reindex
操作外,还有什么方法能够解决这个问题呢?此处咱们通过 runtime field
来解决。
2、runtime field 介绍
2.1 runtime field 能够实现的性能
运行时字段是在查问时评估的字段。是在 es7.11 之后减少的
运行时字段使您可能:
- 将字段增加到现有文档,而无需从新索引数据
- 在不理解数据结构的状况下开始解决数据
- 在查问时笼罩从索引字段返回的值
-
定义特定用处的字段,而不批改原始 mapping
2.2 runtime field 优缺点
- runtime field 是运行时减少的字段,不会被索引和存储,不会减少索引的大小。
- runtime field 能够像一般字段一样应用,能够进行
查问
,排序
,聚合
等操作。 - 能够动静的增加字段。
- 能够在查问时笼罩字段的值。即
fields
中和_source
中能够返回同名的字段,然而值可能不一样。 - 阻止 mapping 爆炸,能够先应用后定义。
- 针对常常被搜寻或聚合等操作的字段,不适宜应用 runtime field,而应该定义在 mapping 中。
- runtime field 不会呈现在
_source
中,须要通过fields api
来获取。
3、创立 runtime field 的形式
3.1 通过 mapping 的形式创立
3.1.1、增加 runtime field
PUT /index_script_fields
{
"mappings": {
"runtime": {
"aggLineName": {
"type": "keyword",
"script": {"source": "emit(doc['lineName'].value)"
}
}
},
"properties": {
"lineId": {"type": "keyword"},
"lineName": {"type": "text"}
}
}
}
3.1.2、更新 runtime field
POST /index_script_fields/_mapping
{
"runtime": {
"aggLineName": {
"type": "keyword",
"script": {"source": "emit(doc['lineName'].value)"
}
}
}
}
3.1.3、删除 runtime field
POST /index_script_fields/_mapping
{
"runtime": {"aggLineName": null}
}
3.2 通过 search request 定义 runtime field
GET /index_script_fields/_search
{
"runtime_mappings": {
"lineName": {
"type": "keyword",
"script": "emit(params['_source']['lineName']+'new')"
}
},
"query": {"match_all": {}
},
"fields": ["lineName"]
}
4、需要
咱们存在一个线路 mapping,其中 lineName
在设计的应用应用了 text
类型,当初咱们须要依据这个字段来进行聚合操作,那么应用 runtime field
该如何操作呢?
5、实现
5.1 mapping
PUT /index_script_fields
{
"mappings": {
"properties": {
"lineId": {"type": "keyword"},
"lineName": {"type": "text"}
}
}
}
留神此时的 lineName
的类型是text
5.2 插入数据
PUT /index_script_fields/_bulk
{"index":{"_id":1}}
{"lineId":"line-01","lineName":"线路 A"}
{"index":{"_id":2}}
{"lineId":"line-01","lineName":"线路 A"}
{"index":{"_id":3}}
{"lineId":"line-02","lineName":"线路 C"}
5.3、依据线路来进行聚合
从上方的 mapping
中能够 lineName
是text
类型,是不可进行聚合操作的,那么此时咱们想进行聚合操作,就能够应用 runtime field
来实现。
5.3.1 不应用 runtime field
5.3.2 应用 runtime field
5.3.2.1 dsl
GET /index_script_fields/_search
{
"runtime_mappings": {
"aggLineName": {
"type": "keyword",
"script": "emit(params['_source']['lineName']+'new')"
}
},
"query": {"match_all": {}
},
"fields": ["lineName"],
"aggs": {
"agg_line_name": {
"terms": {
"field": "aggLineName",
"size": 10
}
}
}
}
5.3.2.2 java 代码
@Test
@DisplayName("lineName 字段是 text 类型,无奈进行聚合操作,定义一个 runtime field 来进行聚合操作")
public void test01() throws IOException {
SearchRequest request = SearchRequest.of(searchRequest ->
searchRequest.index(INDEX_NAME)
// 查问所有数据
.query(query -> query.matchAll(matchAll -> matchAll))
// runtime field 字段不会呈现在 _source 中,须要应用应用 fields api 来获取
.fields(fields -> fields.field("lineName"))
// 创立一个 runtime filed 字段类型是 keyword
.runtimeMappings("aggLineName", runtime ->
runtime
// 此处给字段类型为 keyword
.type(RuntimeFieldType.Keyword)
.script(script ->
script.inline(inline ->
// runtime field 中如果应用 painless 脚本语言,须要应用 emit
inline.lang(ScriptLanguage.Painless)
.source("emit(params['_source']['lineName']+'new')")
)
)
)
// 进行聚合操作
.aggregations("agg_line_name", agg ->
// 此处的 aggLineName 即为上一步 runtime field 的字段
agg.terms(terms -> terms.field("aggLineName").size(10))
)
.size(100)
);
System.out.println("request:" + request);
SearchResponse<Object> response = client.search(request, Object.class);
System.out.println("response:" + response);
5.3.3.3 运行后果
6、残缺代码
https://gitee.com/huan1993/spring-cloud-parent/blob/master/es/es8-api/src/main/java/com/huan/es8/runtimefield/RuntimeFieldCorrectMappingError.java
7、参考链接
1、https://www.elastic.co/guide/en/elasticsearch/reference/8.6/runtime.html