在我們使用es的開(kāi)發(fā)過(guò)程中可能會(huì)遇到這么一種情況,比如我們的線(xiàn)路名稱(chēng)字段lineName字段在設(shè)置mapping的時(shí)候使用的是text類(lèi)型,但是后期發(fā)現(xiàn)需要使用這個(gè)字段來(lái)進(jìn)行聚合操作,那么我們除了對(duì)索引進(jìn)行reindex操作外,還有什么辦法可以解決這個(gè)問(wèn)題呢?
1、背景
在我們使用es的開(kāi)發(fā)過(guò)程中可能會(huì)遇到這么一種情況,比如我們的線(xiàn)路名稱(chēng)字段lineName字段在設(shè)置mapping的時(shí)候使用的是text類(lèi)型,但是后期發(fā)現(xiàn)需要使用這個(gè)字段來(lái)進(jìn)行聚合操作,那么我們除了對(duì)索引進(jìn)行reindex操作外,還有什么辦法可以解決這個(gè)問(wèn)題呢?此處我們通過(guò)runtime field來(lái)解決。
2、runtime field介紹
2.1 runtime field可以實(shí)現(xiàn)的功能
運(yùn)行時(shí)字段是在查詢(xún)時(shí)評(píng)估的字段。是在es7.11之后增加的運(yùn)行時(shí)字段使您能夠:
- 將字段添加到現(xiàn)有文檔,而無(wú)需重新索引數(shù)據(jù)
- 在不了解數(shù)據(jù)結(jié)構(gòu)的情況下開(kāi)始處理數(shù)據(jù)
- 在查詢(xún)時(shí)覆蓋從索引字段返回的值
- 定義特定用途的字段,而不修改原始mappingruntime field 的作用
2.2 runtime field優(yōu)缺點(diǎn)
- runtime field是運(yùn)行時(shí)增加的字段,不會(huì)被索引和存儲(chǔ),不會(huì)增加索引的大小。
- runtime field 可以像普通字段一樣使用,可以進(jìn)行查詢(xún),排序,聚合等操作。
- 可以動(dòng)態(tài)的添加字段。
- 可以在查詢(xún)時(shí)覆蓋字段的值。即fields中和_source中可以返回同名的字段,但是值可能不一樣。
- 阻止mapping爆炸,可以先使用后定義。
- 針對(duì)經(jīng)常被搜索或聚合等操作的字段,不適合使用runtime field,而應(yīng)該定義在mapping中。
- runtime field不會(huì)出現(xiàn)在_source中,需要通過(guò)fields api來(lái)獲取。
3、創(chuàng)建runtime field的方式
3.1 通過(guò)mapping的方式創(chuàng)建
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 通過(guò)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"
]
}

通過(guò)search request定義runtime field
4、需求
我們存在一個(gè)線(xiàn)路mapping,其中lineName在設(shè)計(jì)的使用使用了text類(lèi)型,現(xiàn)在我們需要根據(jù)這個(gè)字段來(lái)進(jìn)行聚合操作,那么使用runtime field該如何操作呢?
5、實(shí)現(xiàn)
5.1 mapping
PUT /index_script_fields
{
"mappings": {
"properties": {
"lineId": {
"type": "keyword"
},
"lineName": {
"type": "text"
}
}
}
}
注意此時(shí)的lineName的類(lèi)型是text
5.2 插入數(shù)據(jù)
PUT /index_script_fields/_bulk
{"index":{"_id":1}}
{"lineId":"line-01","lineName":"線(xiàn)路A"}
{"index":{"_id":2}}
{"lineId":"line-01","lineName":"線(xiàn)路A"}
{"index":{"_id":3}}
{"lineId":"line-02","lineName":"線(xiàn)路C"}
5.3、根據(jù)線(xiàn)路來(lái)進(jìn)行聚合
從上方的mapping中可以lineName是text類(lèi)型,是不可進(jìn)行聚合操作的,那么此時(shí)我們想進(jìn)行聚合操作,就可以使用runtime field來(lái)實(shí)現(xiàn)。
5.3.1 不使用runtime field

不使用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類(lèi)型,無(wú)法進(jìn)行聚合操作,定義一個(gè)runtime field來(lái)進(jìn)行聚合操作")
public void test01() throws IOException {
SearchRequest request = SearchRequest.of(searchRequest ->
searchRequest.index(INDEX_NAME)
// 查詢(xún)所有數(shù)據(jù)
.query(query -> query.matchAll(matchAll -> matchAll))
// runtime field字段不會(huì)出現(xiàn)在 _source中,需要使用使用 fields api來(lái)獲取
.fields(fields -> fields.field("lineName"))
// 創(chuàng)建一個(gè) runtime filed 字段類(lèi)型是 keyword
.runtimeMappings("aggLineName", runtime ->
runtime
// 此處給字段類(lèi)型為keyword
.type(RuntimeFieldType.Keyword)
.script(script ->
script.inline(inline ->
// runtime field中如果使用 painless腳本語(yǔ)言,需要使用emit
inline.lang(ScriptLanguage.Painless)
.source("emit(params['_source']['lineName']+'new')")
)
)
)
// 進(jìn)行聚合操作
.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 運(yùn)行結(jié)果

聚合
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?