乐趣区

关于后端:elasticsearch中使用runtime-fields

1、背景

在咱们应用 es 的开发过程中可能会遇到这么一种状况,比方咱们的线路名称字段 lineName 字段在设置 mapping 的时候应用的是 text 类型,然而前期发现须要应用这个字段来进行 聚合操作 ,那么咱们除了对索引进行reindex 操作外,还有什么方法能够解决这个问题呢?此处咱们通过 runtime field 来解决。

2、runtime field 介绍

2.1 runtime field 能够实现的性能

运行时字段是在查问时评估的字段。是在 es7.11 之后减少的 运行时字段使您可能:

  1. 将字段增加到现有文档,而无需从新索引数据
  2. 在不理解数据结构的状况下开始解决数据
  3. 在查问时笼罩从索引字段返回的值
  4. 定义特定用处的字段,而不批改原始 mapping

    2.2 runtime field 优缺点

  5. runtime field 是运行时减少的字段,不会被索引和存储,不会减少索引的大小。
  6. runtime field 能够像一般字段一样应用,能够进行 查问 , 排序 , 聚合 等操作。
  7. 能够动静的增加字段。
  8. 能够在查问时笼罩字段的值。即 fields 中和 _source 中能够返回同名的字段,然而值可能不一样。
  9. 阻止 mapping 爆炸,能够先应用后定义。
  10. 针对常常被搜寻或聚合等操作的字段,不适宜应用 runtime field,而应该定义在 mapping 中。
  11. 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 中能够 lineNametext类型,是不可进行聚合操作的,那么此时咱们想进行聚合操作,就能够应用 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

退出移动版