Java代码中ElasticSearch Fielddata异常修复及BulkRequest映射配置
解决Elasticsearch的Fielddata异常及BulkRequest映射配置问题
一、如何设置Fielddata为true?
你碰到的这个异常,本质是Elasticsearch默认关闭了text字段的fielddata功能——毕竟text字段主打全文检索,开启fielddata会占用大量堆内存,所以默认禁用。如果确实需要对text字段做排序、count聚合这类操作,有两种可行的开启方式:
1. 修改已存在索引的字段映射
如果你的索引已经创建完成,可以通过以下代码更新目标字段的映射(假设要开启的字段名为your_text_field):
PutMappingRequest mappingRequest = new PutMappingRequest(indexName) .source(XContentFactory.jsonBuilder() .startObject() .startObject("properties") .startObject("your_text_field") .field("type", "text") .field("fielddata", true) .endObject() .endObject() .endObject()); client.indices().putMapping(mappingRequest, RequestOptions.DEFAULT);
2. 创建索引时直接配置映射(推荐)
如果索引还未创建,建议在初始化阶段就把需要开启fielddata的字段配置好,避免后续修改映射的麻烦:
CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName); createIndexRequest.mapping(XContentFactory.jsonBuilder() .startObject() .startObject("properties") .startObject("your_text_field") .field("type", "text") .field("fielddata", true) // 直接开启fielddata .endObject() // 其他字段的映射配置... .endObject() .endObject()); client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
二、如何为BulkRequest添加映射?
这里要明确:BulkRequest本身不能直接添加映射,映射是索引级别的配置,必须先创建索引并设置好映射,再执行批量插入操作。你现有代码直接用BulkRequest插入数据的话,如果索引不存在,Elasticsearch会自动创建索引并根据第一条数据生成默认映射,但这种自动生成的映射往往不符合需求(比如text字段默认关闭fielddata)。
所以修改你的代码可以按以下步骤来:
- 先检查索引是否存在,不存在则创建并配置好映射
- 再执行批量插入操作
修改后的完整代码示例:
// 1. 先处理索引与映射配置 String indexName = "your_index"; // 注意:Elasticsearch 7+已废弃type概念,建议用"_doc"或直接省略 String type = "_doc"; // 检查索引是否存在 if (!client.indices().exists(new GetIndexRequest(indexName), RequestOptions.DEFAULT)) { // 创建索引并自定义映射 CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName); createIndexRequest.mapping(XContentFactory.jsonBuilder() .startObject() .startObject("properties") // 配置需要开启fielddata的text字段 .startObject("title") .field("type", "text") .field("fielddata", true) .endObject() .startObject("content") .field("type", "text") .endObject() .startObject("create_time") .field("type", "date") .endObject() // 其他字段配置... .endObject() .endObject()); client.indices().create(createIndexRequest, RequestOptions.DEFAULT); } // 2. 执行批量插入操作 BulkRequest request = new BulkRequest(); try { BufferedReader br = new BufferedReader(new FileReader(fileName)); String line; while ((line = br.readLine()) != null) { // ES 7+推荐的IndexRequest写法(省略type参数) request.add(new IndexRequest(indexName).source(line, XContentType.JSON)); } BulkResponse bulkresp = client.bulk(request, RequestOptions.DEFAULT); afterBulk(request, bulkresp); br.close(); // 记得关闭流资源 } catch (IOException e) { e.printStackTrace(); }
额外提醒
开启fielddata要谨慎,它会把字段的分词结果加载到堆内存,数据量大时容易引发OOM。如果只是需要对字段做排序或聚合,更推荐的方式是给text字段添加一个keyword子字段,用keyword字段来完成排序/聚合操作,这样更节省内存:
createIndexRequest.mapping(XContentFactory.jsonBuilder() .startObject() .startObject("properties") .startObject("your_text_field") .field("type", "text") .startObject("fields") .startObject("keyword") .field("type", "keyword") .field("ignore_above", 256) .endObject() .endObject() .endObject() .endObject() .endObject());
之后排序或聚合时,使用your_text_field.keyword作为目标字段即可,无需开启fielddata。
内容的提问来源于stack exchange,提问作者Judy
相关产品推荐
相关产品推荐

