依赖配置
Maven 项目
在 pom.xml 文件中添加以下依赖:
xml
co.elastic.clients
elasticsearch-java
8.1.0
com.fasterxml.jackson.core
jackson-databind
2.13.0
org.apache.httpcomponents.client5
httpclient5
5.1.3
org.elasticsearch.client
elasticsearch-rest-client
8.1.0
Gradle 项目
在 build.gradle 文件中添加以下依赖:
groovy
dependencies {
implementation 'co.elastic.clients:elasticsearch-java:8.1.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
implementation 'org.apache.httpcomponents.client5:httpclient5:5.1.3'
implementation 'org.elasticsearch.client:elasticsearch-rest-client:8.1.0'
}
客户端初始化
基本客户端初始化
java
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
public class ElasticsearchClientUtil {
/**
* 创建 Elasticsearch 客户端(无认证)
*/
public static ElasticsearchClient createClient() {
// 创建 REST 客户端
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200, "http")
).build();
// 创建传输层
RestClientTransport transport = new RestClientTransport(
restClient,
new JacksonJsonpMapper()
);
// 创建 Elasticsearch 客户端
return new ElasticsearchClient(transport);
}
/**
* 创建 Elasticsearch 客户端(带认证)
*/
public static ElasticsearchClient createClientWithAuth(
String host, int port, String scheme,
String username, String password) {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(username, password)
);
RestClientBuilder builder = RestClient.builder(
new HttpHost(host, port, scheme)
);
builder.setHttpClientConfigCallback(httpClientBuilder ->
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
);
RestClient restClient = builder.build();
RestClientTransport transport = new RestClientTransport(
restClient,
new JacksonJsonpMapper()
);
return new ElasticsearchClient(transport);
}
/**
* 关闭客户端
*/
public static void closeClient(ElasticsearchClient client) throws IOException {
if (client != null) {
client._transport().close();
}
}
}
带 SSL/TLS 的客户端初始化
java
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.ssl.SSLContextBuilder;
public class ElasticsearchClientUtil {
/**
* 创建带 SSL 的 Elasticsearch 客户端
*/
public static ElasticsearchClient createClientWithSSL(
String host, int port, String caCertPath) throws Exception {
SSLContextBuilder sslBuilder = SSLContextBuilder.create()
.loadTrustMaterial(new File(caCertPath),
new TrustSelfSignedStrategy());
RestClientBuilder builder = RestClient.builder(
new HttpHost(host, port, "https")
);
builder.setHttpClientConfigCallback(httpClientBuilder ->
httpClientBuilder.setSSLContext(sslBuilder.build())
);
RestClient restClient = builder.build();
RestClientTransport transport = new RestClientTransport(
restClient,
new JacksonJsonpMapper()
);
return new ElasticsearchClient(transport);
}
}
索引操作 API
Elasticsearch 索引知识点
什么是索引(Index)?
在 Elasticsearch 中,索引(Index)类似于关系型数据库中的数据库(Database),是文档的集合。每个索引都有自己的映射(Mapping)和设置(Settings)。
关键概念:
- 索引名称:必须小写,不能包含特殊字符
- 索引别名(Alias):可以给索引起一个别名,方便索引切换
- 索引模板(Template):可以定义索引模板,自动创建符合规则的索引
映射(Mapping)
映射(Mapping)定义了索引中文档的结构,类似于关系型数据库中的表结构(Schema)。映射包括:
- 字段类型:text、keyword、long、integer、date 等
- 分析器(Analyzer):用于索引时的文本分析
- 搜索分析器(Search Analyzer):用于搜索时的文本分析
常见字段类型:
text:全文搜索字段,会被分词keyword:精确匹配字段,不分词long/integer/short/byte:数值类型date:日期类型boolean:布尔类型object:对象类型(嵌套文档)nested:嵌套类型(数组对象)
索引设置(Settings)
索引设置控制索引的行为,包括:
- 分片数(number_of_shards):索引的主分片数量,创建后不可修改
- 副本数(number_of_replicas):每个主分片的副本数量,可以动态修改
- 刷新间隔(refresh_interval):数据写入后多久可以被搜索到
- 分析器配置:自定义分析器、分词器等
分片(Shard)和副本(Replica):
- 主分片(Primary Shard):存储实际数据,分片数量决定索引的最大数据量
- 副本分片(Replica Shard):主分片的副本,提供高可用性和读取性能
- 分片数量建议:根据数据量和节点数量合理设置,通常每个分片 20-50GB
索引生命周期
索引可以有以下状态:
- 打开(Open):正常状态,可以读写
- 关闭(Close):索引关闭后不能读写,但数据仍然存在,可以节省资源
- 只读(Read-only):只能读取,不能写入
1. 创建索引
1.1 创建简单索引
java
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
import co.elastic.clients.elasticsearch.indices.ElasticsearchIndicesClient;
import java.io.IOException;
public class IndexOperations {
/**
* 创建简单索引(无映射)
*/
public static boolean createIndex(ElasticsearchClient client, String indexName)
throws IOException {
CreateIndexResponse response = client.indices().create(c -> c
.index(indexName)
);
return response.acknowledged();
}
}
1.2 创建带映射的索引
java
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.json.JsonData;
public class IndexOperations {
/**
* 创建带映射的索引
*/
public static boolean createIndexWithMapping(
ElasticsearchClient client, String indexName) throws IOException {
CreateIndexResponse response = client.indices().create(c -> c
.index(indexName)
.mappings(m -> m
.properties("id", p -> p.long_(l -> l))
.properties("title", p -> p
.text(t -> t
.analyzer("ik_max_word")
.searchAnalyzer("ik_smart")
)
)
.properties("content", p -> p
.text(t -> t
.analyzer("ik_max_word")
.searchAnalyzer("ik_smart")
)
)
.properties("author", p -> p.keyword(k -> k))
.properties("createTime", p -> p.date(d -> d.format("yyyy-MM-dd HH:mm:ss")))
.properties("views", p -> p.integer(i -> i))
)
);
return response.acknowledged();
}
}
1.3 创建带设置的索引
java
import co.elastic.clients.elasticsearch.indices.IndexSettings;
public class IndexOperations {
/**
* 创建带设置的索引(分片数、副本数等)
*/
public static boolean createIndexWithSettings(
ElasticsearchClient client, String indexName) throws IOException {
CreateIndexResponse response = client.indices().create(c -> c
.index(indexName)
.settings(s -> s
.numberOfShards("3")
.numberOfReplicas("1")
.analysis(a -> a
.analyzer("ik_max_word", an -> an
.custom(cu -> cu
.tokenizer("ik_max_word")
)
)
)
)
);
return response.acknowledged();
}
}
2. 检查索引是否存在
java
public class IndexOperations {
/**
* 检查索引是否存在
*/
public static boolean indexExists(ElasticsearchClient client, String indexName)
throws IOException {
return client.indices().exists(e -> e.index(indexName)).value();
}
}
3. 获取索引信息
java
import co.elastic.clients.elasticsearch.indices.GetIndexResponse;
public class IndexOperations {
/**
* 获取索引信息
*/
public static GetIndexResponse getIndex(
ElasticsearchClient client, String indexName) throws IOException {
return client.indices().get(g -> g.index(indexName));
}
}
4. 获取索引映射
java
import co.elastic.clients.elasticsearch.indices.GetMappingResponse;
public class IndexOperations {
/**
* 获取索引映射
*/
public static GetMappingResponse getIndexMapping(
ElasticsearchClient client, String indexName) throws IOException {
return client.indices().getMapping(g -> g.index(indexName));
}
}
5. 更新索引映射
java
import co.elastic.clients.elasticsearch.indices.PutMappingResponse;
public class IndexOperations {
/**
* 更新索引映射(添加新字段)
*/
public static boolean updateIndexMapping(
ElasticsearchClient client, String indexName) throws IOException {
PutMappingResponse response = client.indices().putMapping(p -> p
.index(indexName)
.properties("newField", pr -> pr.keyword(k -> k))
);
return response.acknowledged();
}
}
6. 更新索引设置
java
import co.elastic.clients.elasticsearch.indices.UpdateIndicesSettingsResponse;
public class IndexOperations {
/**
* 更新索引设置
*/
public static boolean updateIndexSettings(
ElasticsearchClient client, String indexName) throws IOException {
UpdateIndicesSettingsResponse response = client.indices().putSettings(p -> p
.index(indexName)
.settings(s -> s
.numberOfReplicas("2")
)
);
return response.acknowledged();
}
}
7. 删除索引
java
import co.elastic.clients.elasticsearch.indices.DeleteIndexResponse;
public class IndexOperations {
/**
* 删除索引
*/
public static boolean deleteIndex(ElasticsearchClient client, String indexName)
throws IOException {
DeleteIndexResponse response = client.indices().delete(d -> d.index(indexName));
return response.acknowledged();
}
}
8. 批量删除索引
java
public class IndexOperations {
/**
* 批量删除索引
*/
public static boolean deleteIndices(
ElasticsearchClient client, String... indexNames) throws IOException {
DeleteIndexResponse response = client.indices().delete(d -> d
.index(Arrays.asList(indexNames))
);
return response.acknowledged();
}
}
9. 打开/关闭索引
java
import co.elastic.clients.elasticsearch.indices.CloseIndexResponse;
import co.elastic.clients.elasticsearch.indices.OpenIndexResponse;
public class IndexOperations {
/**
* 关闭索引
*/
public static boolean closeIndex(ElasticsearchClient client, String indexName)
throws IOException {
CloseIndexResponse response = client.indices().close(c -> c.index(indexName));
return response.acknowledged();
}
/**
* 打开索引
*/
public static boolean openIndex(ElasticsearchClient client, String indexName)
throws IOException {
OpenIndexResponse response = client.indices().open(o -> o.index(indexName));
return response.acknowledged();
}
}
10. 刷新索引
java
import co.elastic.clients.elasticsearch.indices.RefreshResponse;
public class IndexOperations {
/**
* 刷新索引(使数据立即可搜索)
*/
public static boolean refreshIndex(ElasticsearchClient client, String indexName)
throws IOException {
RefreshResponse response = client.indices().refresh(r -> r.index(indexName));
return response.shards().total() > 0;
}
}
为什么需要专门的索引刷新api? ES 基于 Lucene 构建,数据写入并非直接落盘并立即可查,而是先写入内存缓冲区,默认每隔 1 秒 Lucene 会将缓冲区数据刷入「分段文件(Segment)」,这个过程就是「Refresh」。 未刷新前:数据仅在内存缓冲区,执行 search 查询无法查到; 刷新后:数据进入分段文件,成为可查询的 "可见数据"(但此时数据尚未持久化到磁盘,仅在文件系统缓存,持久化靠 flush 操作)。 这是 ES 为了平衡写入性能和查询实时性的设计:如果每次写入都立即刷新,会产生大量小分段文件,严重影响性能;如果完全依赖 1 秒自动刷新,又无法满足 "写入后立即查询" 的场景(如秒杀、实时日志检索)。
文档操作 API
Elasticsearch 文档知识点
什么是文档(Document)?
在 Elasticsearch 中,文档(Document)是索引中的基本数据单元,类似于关系型数据库中的行(Row)。文档是 JSON 格式的,包含多个字段(Field)。
文档结构:
- 索引(_index):文档所属的索引
- 类型(_type):在 ES 7.x 之前有类型概念,8.x 中已废弃
- ID(_id):文档的唯一标识符
- 版本(_version):文档的版本号,用于乐观锁控制
- 源数据(_source):文档的原始 JSON 数据
文档操作类型
Elasticsearch 支持以下文档操作:
- Index(索引):创建或更新文档(如果文档已存在则替换)
- Create(创建):仅创建文档(如果文档已存在则失败)
- Update(更新):部分更新文档
- Delete(删除):删除文档
- Get(获取):根据 ID 获取文档
版本控制(Versioning)
Elasticsearch 使用版本号来管理文档的并发更新:
- 内部版本(_version):每次更新自动递增
- 外部版本(external version):可以使用外部系统提供的版本号
- 乐观锁:通过版本号实现乐观并发控制
文档路由(Routing)
文档会被路由到特定的分片,路由规则:
- 默认使用文档 ID 的哈希值:
shard_num = hash(_routing) % num_primary_shards - 可以自定义路由值,相同路由值的文档会存储在同一分片
- 路由可以提高查询性能(减少查询的分片数量)
文档刷新(Refresh)
文档写入后不会立即可搜索,需要等待刷新:
- 自动刷新:默认每 1 秒自动刷新一次
- 手动刷新:调用 refresh API 立即刷新
- 实时性 vs 性能:刷新间隔越短,实时性越好,但性能会下降
1. 索引文档(新增/更新)
1.1 使用自动生成的 ID
java
import co.elastic.clients.elasticsearch.core.IndexResponse;
public class DocumentOperations {
/**
* 索引文档(自动生成 ID)
*/
public static String indexDocument(
ElasticsearchClient client, String indexName, Object document)
throws IOException {
IndexResponse response = client.index(i -> i
.index(indexName)
.document(document)
);
return response.id();
}
}
1.2 使用指定 ID
java
public class DocumentOperations {
/**
* 索引文档(指定 ID)
*/
public static String indexDocumentWithId(
ElasticsearchClient client, String indexName, String id, Object document)
throws IOException {
IndexResponse response = client.index(i -> i
.index(indexName)
.id(id)
.document(document)
);
return response.id();
}
}
1.3 使用 Map 作为文档
java
import java.util.HashMap;
import java.util.Map;
public class DocumentOperations {
/**
* 使用 Map 索引文档
*/
public static String indexDocumentFromMap(
ElasticsearchClient client, String indexName, String id,
Map documentMap) throws IOException {
IndexResponse response = client.index(i -> i
.index(indexName)
.id(id)
.document(documentMap)
);
return response.id();
}
}
1.4 使用 POJO 作为文档
java
// 定义 POJO 类
public class Article {
private String id;
private String title;
private String content;
private String author;
private Long views;
private String createTime;
// 构造函数、getter 和 setter
public Article() {}
public Article(String id, String title, String content,
String author, Long views, String createTime) {
this.id = id;
this.title = title;
this.content = content;
this.author = author;
this.views = views;
this.createTime = createTime;
}
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public Long getViews() { return views; }
public void setViews(Long views) { this.views = views; }
public String getCreateTime() { return createTime; }
public void setCreateTime(String createTime) { this.createTime = createTime; }
}
public class DocumentOperations {
/**
* 使用 POJO 索引文档
*/
public static String indexDocumentFromPOJO(
ElasticsearchClient client, String indexName, Article article)
throws IOException {
IndexResponse response = client.index(i -> i
.index(indexName)
.id(article.getId())
.document(article)
);
return response.id();
}
}
2. 获取文档
2.1 根据 ID 获取文档
java
import co.elastic.clients.elasticsearch.core.GetResponse;
public class DocumentOperations {
/**
* 根据 ID 获取文档
*/
public static T getDocument(
ElasticsearchClient client, String indexName, String id, Class clazz)
throws IOException {
GetResponse response = client.get(g -> g
.index(indexName)
.id(id),
clazz
);
if (response.found()) {
return response.source();
}
return null;
}
}
clazz参数的作用: 指定返回对象的类型:clazz 是一个 Class 类型的参数,表示调用者期望从 Elasticsearch 中获取文档并将其反序列化为哪种 Java 对象类型。
2.2 检查文档是否存在
java
public class DocumentOperations {
/**
* 检查文档是否存在
*/
public static boolean documentExists(
ElasticsearchClient client, String indexName, String id)
throws IOException {
return client.exists(e -> e
.index(indexName)
.id(id)
).value();
}
}
3. 更新文档
知识点:文档更新
- 更新方式 :
- Index API:完全替换文档(如果文档不存在则创建)
- Update API:部分更新文档(只更新指定字段)
- Update API 优势 :
- 只传输需要更新的字段,减少网络开销
- 支持脚本更新(如:增加字段值)
- 支持 upsert(如果文档不存在则创建)
- 更新过程 :
- 获取原文档
- 合并更新字段
- 重新索引文档
- 版本号自动递增
- 版本控制:更新时会检查版本号,防止并发冲突
3.1 使用 Update API 更新文档
java
import co.elastic.clients.elasticsearch.core.UpdateResponse;
import co.elastic.clients.json.JsonData;
public class DocumentOperations {
/**
* 更新文档(部分字段)
*/
public static String updateDocument(
ElasticsearchClient client, String indexName, String id,
Map updateFields) throws IOException {
UpdateResponse response = client.update(u -> u
.index(indexName)
.id(id)
.doc(updateFields),
Object.class
);
return response.id();
}
/**
* 使用脚本更新文档
*/
public static String updateDocumentWithScript(
ElasticsearchClient client, String indexName, String id,
String script) throws IOException {
UpdateResponse response = client.update(u -> u
.index(indexName)
.id(id)
.script(s -> s
.inline(i -> i.source(script))
),
Object.class
);
return response.id();
}
/**
* 增加字段值(例如:增加浏览次数)
*/
public static String incrementField(
ElasticsearchClient client, String indexName, String id,
String fieldName, int increment) throws IOException {
String script = "ctx._source." + fieldName + " += params.increment";
Map params = new HashMap<>();
params.put("increment", JsonData.of(increment));
UpdateResponse response = client.update(u -> u
.index(indexName)
.id(id)
.script(s -> s
.inline(i -> i
.source(script)
.params(params)
)
),
Object.class
);
return response.id();
}
}
拓展知识:es8.x中的update脚本知识:
ES 8.x 中 update 脚本的核心规则: 脚本语言默认是 painless,无需额外指定; 通过 ctx._source 操作文档的字段(ctx 是上下文对象,_source 对应文档原始数据); 支持新增字段、修改字段、删除字段、数值运算、条件判断等逻辑。 示例 1:新增 / 修改单个字段 脚本内容(字符串形式)
java
运行
String script1 = "ctx._source.new_field = '新增的字符串值'; ctx._source.old_field = '修改后的旧字段值'";
调用方式
运行 // 调用方法更新文档 String docId = updateDocumentWithScript(client, "my_index", "1001", script1); 效果 若文档无 new_field,则新增该字段并赋值; 若已有 old_field,则覆盖其值;若无则新增。 示例 2:数值字段自增 / 自减 脚本内容
java
运行
// 订单金额加10,库存减1
String script2 = "ctx._source.amount += 10; ctx._source.stock -= 1";
效果 适用于计数、库存、金额等数值型字段的增量更新; 注意:若字段不存在,会抛出 NullPointerException,可加判空(见示例 4)。
示例 3:删除字段 脚本内容
java
运行
// 删除无用字段
String script3 = "ctx._source.remove('useless_field')";
效果 从文档 _source 中移除指定字段,更新后查询将不再返回该字段。
示例 4:条件更新(满足条件才修改) 脚本内容
java
运行
// 仅当状态为待支付时,更新支付时间和状态
String script4 = """
if (ctx._source.status == 'PENDING') {
ctx._source.status = 'PAID';
ctx._source.pay_time = new Date().getTime(); // 赋值当前时间戳
}
""";
注意 多行脚本建议用 Java 文本块("""),避免拼接转义符; new Date().getTime() 是 Painless 支持的时间操作,也可传入外部参数(见示例 5)。 示例 5:带参数的脚本(推荐,避免硬编码) 优化点 直接在脚本中写死值易维护性差,ES 支持通过 params 传入参数,更灵活且避免脚本注入风险。 改造方法(需微调原方法,兼容参数传递) 先修改原方法以支持参数:
java
运行
/**
* 支持参数的脚本更新(更通用)
*/
public static String updateDocumentWithScript(
ElasticsearchClient client, String indexName, String id,
String script, Map params) throws IOException {
UpdateResponse response = client.update(u -> u
.index(indexName)
.id(id)
.script(s -> s
.inline(i -> i
.source(script)
.params(params) // 传入参数
)
),
Object.class
);
return response.id();
}
脚本 + 参数调用
java
运行
// 脚本(使用参数占位符)
String script5 = "ctx._source.discount = params.discount; ctx._source.price = ctx._source.original_price * (1 - params.discount)";
// 参数映射
Map params = new HashMap<>();
params.put("discount", 0.2); // 20% 折扣
// 调用更新
String docId = updateDocumentWithScript(client, "my_index", "1001", script5, params);
效果 动态传入折扣率,计算最终价格; 脚本逻辑与数据分离,复用性更高。 示例 6:数组字段操作 脚本内容
java
运行
// 向数组添加元素(去重)+ 删除指定元素
String script6 = """
// 确保数组存在,不存在则初始化
if (!ctx._source.containsKey('tags')) {
ctx._source.tags = [];
}
// 去重添加
if (!ctx._source.tags.contains('es')) {
ctx._source.tags.add('es');
}
// 删除元素
ctx._source.tags.removeIf(tag -> tag == 'old_tag');
""";
效果 处理标签、关键词等数组类型字段,避免重复添加; 支持数组元素的增删改查。
4. 删除文档
java
import co.elastic.clients.elasticsearch.core.DeleteResponse;
public class DocumentOperations {
/**
* 根据 ID 删除文档
*/
public static boolean deleteDocument(
ElasticsearchClient client, String indexName, String id)
throws IOException {
DeleteResponse response = client.delete(d -> d
.index(indexName)
.id(id)
);
return response.result().name().equals("Deleted");
}
}
5. 批量操作
5.1 批量索引文档
java
import co.elastic.clients.elasticsearch.core.BulkRequest;
import co.elastic.clients.elasticsearch.core.BulkResponse;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
public class DocumentOperations {
/**
* 批量索引文档
*/
public static BulkResponse bulkIndexDocuments(
ElasticsearchClient client, String indexName, List documents)
throws IOException {
BulkRequest.Builder bulkBuilder = new BulkRequest.Builder();
for (Object doc : documents) {
bulkBuilder.operations(op -> op
.index(idx -> idx
.index(indexName)
.document(doc)
)
);
}
BulkResponse response = client.bulk(bulkBuilder.build());
return response;
}
/**
* 批量索引文档(带 ID)
*/
public static BulkResponse bulkIndexDocumentsWithIds(
ElasticsearchClient client, String indexName,
Map documents) throws IOException {
BulkRequest.Builder bulkBuilder = new BulkRequest.Builder();
for (Map.Entry entry : documents.entrySet()) {
bulkBuilder.operations(op -> op
.index(idx -> idx
.index(indexName)
.id(entry.getKey())
.document(entry.getValue())
)
);
}
BulkResponse response = client.bulk(bulkBuilder.build());
return response;
}
}
5.2 批量更新文档
java
public class DocumentOperations {
/**
* 批量更新文档
*/
public static BulkResponse bulkUpdateDocuments(
ElasticsearchClient client, String indexName,
Map> updateMap) throws IOException {
BulkRequest.Builder bulkBuilder = new BulkRequest.Builder();
for (Map.Entry> entry : updateMap.entrySet()) {
bulkBuilder.operations(op -> op
.update(up -> up
.index(indexName)
.id(entry.getKey())
.document(entry.getValue())
)
);
}
BulkResponse response = client.bulk(bulkBuilder.build());
return response;
}
}
5.3 批量删除文档
java
public class DocumentOperations {
/**
* 批量删除文档
*/
public static BulkResponse bulkDeleteDocuments(
ElasticsearchClient client, String indexName, List ids)
throws IOException {
BulkRequest.Builder bulkBuilder = new BulkRequest.Builder();
for (String id : ids) {
bulkBuilder.operations(op -> op
.delete(d -> d
.index(indexName)
.id(id)
)
);
}
BulkResponse response = client.bulk(bulkBuilder.build());
return response;
}
}
5.4 混合批量操作
java
public class DocumentOperations {
/**
* 混合批量操作(索引、更新、删除)
*/
public static BulkResponse bulkMixedOperations(
ElasticsearchClient client, String indexName,
List documentsToIndex,
Map> documentsToUpdate,
List idsToDelete) throws IOException {
BulkRequest.Builder bulkBuilder = new BulkRequest.Builder();
// 批量索引
for (Object doc : documentsToIndex) {
bulkBuilder.operations(op -> op
.index(idx -> idx
.index(indexName)
.document(doc)
)
);
}
// 批量更新
for (Map.Entry> entry : documentsToUpdate.entrySet()) {
bulkBuilder.operations(op -> op
.update(up -> up
.index(indexName)
.id(entry.getKey())
.document(entry.getValue())
)
);
}
// 批量删除
for (String id : idsToDelete) {
bulkBuilder.operations(op -> op
.delete(d -> d
.index(indexName)
.id(id)
)
);
}
BulkResponse response = client.bulk(bulkBuilder.build());
// 检查错误
if (response.errors()) {
response.items().forEach(item -> {
if (item.error() != null) {
System.err.println("错误: " + item.error().reason());
}
});
}
return response;
}
}