> 技术文档 > Java 操作 Elasticsearch 8 + 完全指南:从入门到实战_java 使用elasticsearch8

Java 操作 Elasticsearch 8 + 完全指南:从入门到实战_java 使用elasticsearch8


一、引言:为什么选择 Elasticsearch 8 + 与 Java 客户端

Elasticsearch(简称 ES)作为一款高性能的分布式全文搜索引擎,广泛应用于日志分析、全文检索、数据分析等场景。随着 ES 8.x 版本的发布,其 Java 客户端迎来了重大升级 ——全面采用流式 API(Fluent API),通过链式调用简化操作逻辑,同时增强了类型安全性与代码可读性。

本文将从环境搭建、客户端配置、核心操作(查询、增删改)到进阶实践,全方位讲解如何使用 Java 高效操作 Elasticsearch 8+,帮助开发者快速掌握从入门到实战的完整流程。

二、环境准备与依赖管理

在开始开发前,需确保环境与依赖满足以下要求:

2.1 环境要求

  • JDK 版本:11 及以上(ES 8 + 客户端依赖 JDK 11 的特性)
  • Elasticsearch 版本:8.x(客户端与服务端版本需保持兼容,建议完全一致)
  • 开发工具:IntelliJ IDEA(或其他 Java IDE)、Maven/Gradle

2.2 依赖配置

使用 Maven 管理依赖时,需引入 ES 官方 Java 客户端及 JSON 处理库:

 co.elastic.clients elasticsearch-java 8.1.2 jakarta.json jakarta.json-api 2.0.1 com.fasterxml.jackson.core jackson-databind 2.13.4

注意:ES 客户端版本需与服务端版本严格匹配(如服务端为 8.5.0,客户端也应使用 8.5.0),否则可能出现 API 不兼容问题。

三、客户端配置与初始化

Elasticsearch Java 客户端通过ElasticsearchClient类与服务端交互,其核心是配置RestClient连接池、认证信息及传输层(Transport)。

3.1 配置文件(application.yml)

在 Spring Boot 项目中,通过配置文件存储 ES 连接参数:

elasticsearch: rest: host: 127.0.0.1 # ES服务地址(集群环境可配置多个,用逗号分隔) port: 9200 # 服务端口(默认9200) scheme: http  # 协议(生产环境建议使用https) username: elastic # 认证用户名(ES 8+默认开启安全认证) password: changeme # 认证密码(默认密码需修改) read-timeout: 30 # 读取超时时间(秒) connection-timeout: 5 # 连接超时时间(秒)

3.2 客户端配置类(ElasticSearchConfig)

通过 Spring 配置类初始化ElasticsearchClient,核心是构建RestClientElasticsearchTransport

import co.elastic.clients.elasticsearch.ElasticsearchClient;import co.elastic.clients.json.jackson.JacksonJsonpMapper;import co.elastic.clients.transport.ElasticsearchTransport;import co.elastic.clients.transport.rest_client.RestClientTransport;import org.apache.http.HttpHost;import org.apache.http.auth.AuthScope;import org.apache.http.auth.UsernamePasswordCredentials;import org.apache.http.client.CredentialsProvider;import org.apache.http.impl.client.BasicCredentialsProvider;import org.elasticsearch.client.RestClient;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.time.Duration;/** * Elasticsearch客户端配置类 * 负责创建并管理ElasticsearchClient实例 */@Configurationpublic class ElasticSearchConfig { @Value(\"${elasticsearch.rest.host}\") private String host; @Value(\"${elasticsearch.rest.port}\") private int port; @Value(\"${elasticsearch.rest.scheme}\") private String scheme; @Value(\"${elasticsearch.rest.username}\") private String username; @Value(\"${elasticsearch.rest.password}\") private String password; /** * 初始化ElasticsearchClient * 包含认证配置、连接池优化、保活策略等 */ @Bean public ElasticsearchClient elasticsearchClient() { // 1. 配置账号密码认证(ES 8+默认开启安全机制) final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(username, password) ); // 2. 构建RestClient(HTTP连接层) RestClient restClient = RestClient.builder( new HttpHost(host, port, scheme) // 单节点配置,集群可添加多个HttpHost ) .setHttpClientConfigCallback(httpClientBuilder -> { // 设置认证信息 httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); // 配置连接保活策略(减少频繁重连开销) httpClientBuilder.setKeepAliveStrategy((response, context) ->  Duration.ofMinutes(5).toMillis() // 5分钟保活 ); // 可选:配置连接超时与读取超时 httpClientBuilder.setConnectTimeout(5000); // 连接超时5秒 httpClientBuilder.setSocketTimeout(30000); // 读取超时30秒 return httpClientBuilder; }) .build(); // 3. 配置传输层(JSON序列化/反序列化) ElasticsearchTransport transport = new RestClientTransport( restClient, new JacksonJsonpMapper() // 使用Jackson处理JSON(支持复杂对象映射) ); // 4. 创建客户端实例 return new ElasticsearchClient(transport); }}

核心说明:

 

  • RestClient:负责与 ES 服务端建立 HTTP 连接,支持集群环境配置。
  • ElasticsearchTransport:处理请求 / 响应的序列化与反序列化,默认使用JacksonJsonpMapper(推荐)。
  • 认证配置:ES 8 + 默认启用安全功能,必须配置用户名密码(默认用户为elastic)。

四、核心操作实战:查询与文档管理

通过注入ElasticsearchClient实例,可实现索引查询、文档增删改等核心操作。以下是详细示例:

4.1 数据模型与 DTO 设计

4.1.1 文档实体类(VO)

定义与 ES 索引映射的 Java 实体类(以 “文章搜索” 为例):

import lombok.Data;/** * 文章搜索VO(与ES索引字段映射) */@Datapublic class ArticleSearchVo { private Long id;  // 文章ID private String title; // 标题(分词字段) private String content; // 内容(分词字段) private String author; // 作者(精确匹配字段) private Long publishTime; // 发布时间(时间戳) private String indexName; // 所属索引名(非文档字段,用于标识索引) private String businessType; // 业务类型(用于生成文档ID)}
4.1.2 查询请求 DTO

封装查询参数(关键字、分页、索引名等):

import lombok.Data;/** * ES查询请求DTO */@Datapublic class EsSearchDto { private String keyword; // 搜索关键字 private String indexName; // 目标索引名 private Integer pageNum = 1; // 页码(默认第1页) private Integer pageSize = 10; // 每页条数(默认10条) private String sortField; // 排序字段(可选) private String sortOrder; // 排序方式(asc/desc,可选) /** * 计算ES查询的起始位置(from参数) * from = (pageNum - 1) * pageSize */ public int getFromIndex() { if (pageNum < 1) return 0; if (pageSize < 1) pageSize = 10; return pageSize * (pageNum - 1); }}

4.2 高级查询实战

ES 的查询能力是其核心优势,支持全文检索、组合条件、高亮、分页等功能。以下是常见场景示例:

4.2.1 基础全文检索(match 查询)
/** * 基础全文检索:在title字段中匹配关键字(分词查询) */public List searchByTitle(String indexName, String keyword) { try { SearchResponse response = client.search( s -> s .index(indexName) .query(q -> q  .match(m -> m .field(\"title\") // 目标字段 .query(keyword) // 搜索关键字(会被分词) .fuzziness(\"AUTO\") // 模糊匹配(允许一定拼写错误)  ) ), ArticleSearchVo.class ); // 解析结果 return response.hits().hits().stream() .map(hit -> hit.source()) .collect(Collectors.toList()); } catch (IOException e) { log.error(\"全文检索失败\", e); throw new RuntimeException(\"搜索服务异常\"); }}
4.2.2 组合条件查询(bool + must + should)

实现 “标题或内容包含关键字,且作者为指定值” 的组合查询:

/** * 组合条件查询:多字段OR匹配 + 作者精确匹配 */public AjaxResult complexSearch(EsSearchDto dto) { if (dto == null || dto.getKeyword() == null) { return AjaxResult.error(\"关键字不能为空\"); } try { SearchResponse response = client.search( s -> s .index(dto.getIndexName()) // 布尔查询组合条件 .query(q -> q  .bool(b -> b // must:所有子条件必须匹配(AND) .must(m -> m .term(t -> t // 精确匹配作者 .field(\"author\") .value(dto.getAuthor()) ) ) // should:子条件任意匹配一个(OR) .should(s1 -> s1 // 标题分词匹配 .match(m -> m .field(\"title\") .query(dto.getKeyword()) ) ) .should(s2 -> s2 // 内容分词匹配 .match(m -> m .field(\"content\") .query(dto.getKeyword()) ) ) .should(s3 -> s3 // 标题通配符匹配(包含关键字) .wildcard(w -> w .field(\"title\") .wildcard(\"*\" + dto.getKeyword() + \"*\") ) ) .minimumShouldMatch(\"1\") // 至少匹配1个should条件  ) ) // 分页配置 .from(dto.getFromIndex()) .size(dto.getPageSize()) // 排序(可选) .sort(sort -> {  if (\"asc\".equals(dto.getSortOrder())) { return sort.field(f -> f.field(dto.getSortField()).order(SortOrder.Asc));  } else { return sort.field(f -> f.field(dto.getSortField()).order(SortOrder.Desc));  } }), ArticleSearchVo.class ); // 处理结果 long total = response.hits().total().value(); List list = response.hits().hits().stream() .map(hit -> hit.source()) .collect(Collectors.toList()); return AjaxResult.success() .put(\"total\", total) .put(\"list\", list) .put(\"pages\", (total + dto.getPageSize() - 1) / dto.getPageSize()); } catch (IOException e) { log.error(\"组合查询失败\", e); return AjaxResult.error(\"搜索失败,请重试\"); }}

关键 API 说明:

 

  • bool():用于组合多个查询条件。
  • must():子条件必须全部满足(类似 SQL 的AND)。
  • should():子条件至少满足一个(类似 SQL 的OR),通过minimumShouldMatch设置最小匹配数。
  • term():精确匹配(不分词,适用于 keyword 类型字段)。
  • wildcard():通配符匹配(*匹配任意字符,?匹配单个字符)。
4.2.3 高亮查询(Highlight)

在搜索结果中高亮显示匹配的关键字(如用红色标记):

/** * 高亮查询:在标题和内容中标记匹配的关键字 */public AjaxResult highlightSearch(EsSearchDto dto) { // 高亮标签(前端可通过CSS渲染) String[] preTags = {\"\"}; String[] postTags = {\"\"}; try { SearchResponse response = client.search( s -> s .index(dto.getIndexName()) .query(q -> q  .bool(b -> b .should(s1 -> s1.match(m -> m.field(\"title\").query(dto.getKeyword()))) .should(s2 -> s2.match(m -> m.field(\"content\").query(dto.getKeyword())))  ) ) // 高亮配置 .highlight(h -> h  .fields(\"title\", f -> f.type(\"plain\")) // 对title字段高亮  .fields(\"content\", f -> f.type(\"plain\")) // 对content字段高亮  .preTags(preTags) // 高亮前缀  .postTags(postTags) // 高亮后缀  .requireFieldMatch(false) // 允许高亮未参与查询的字段 ) .from(dto.getFromIndex()) .size(dto.getPageSize()), ArticleSearchVo.class ); // 处理高亮结果:用高亮内容替换原始字段 List resultList = response.hits().hits().stream() .map(hit -> { ArticleSearchVo vo = hit.source(); // 替换标题高亮 if (hit.highlight() != null && hit.highlight().containsKey(\"title\")) {  vo.setTitle(hit.highlight().get(\"title\").get(0)); } // 替换内容高亮 if (hit.highlight() != null && hit.highlight().containsKey(\"content\")) {  vo.setContent(hit.highlight().get(\"content\").get(0)); } return vo; }) .collect(Collectors.toList()); return AjaxResult.success() .put(\"total\", response.hits().total().value()) .put(\"list\", resultList); } catch (IOException e) { log.error(\"高亮查询失败\", e); return AjaxResult.error(\"搜索异常\"); }}

4.3 文档管理(增删改)

4.3.1 新增 / 更新文档
/** * 新增或更新文档(ID存在则更新,否则新增) */public void addOrUpdateDocument(ArticleSearchVo vo) { try { // 生成唯一文档ID:业务ID + 业务类型(避免重复) String docId = vo.getId() + vo.getBusinessType(); // 执行index操作(自动判断新增/更新) IndexResponse response = client.index(i -> i .index(vo.getIndexName()) // 目标索引 .id(docId)  // 文档ID .document(vo)  // 文档内容 .refresh(Refresh.True) // 立即刷新索引(可选,影响性能) ); log.info(\"文档操作成功:索引={}, ID={}\", response.index(), docId); } catch (IOException e) { log.error(\"新增/更新文档失败\", e); // 可根据业务抛出异常或加入重试队列 }}
4.3.2 批量操作(Bulk API)

批量新增 / 更新文档,提升大量数据处理效率:

/** * 批量新增/更新文档 */public void batchAddOrUpdate(List voList) { if (voList == null || voList.isEmpty()) { return; } try { // 构建批量请求 BulkRequest.Builder br = new BulkRequest.Builder(); for (ArticleSearchVo vo : voList) { String docId = vo.getId() + vo.getBusinessType(); // 添加index操作到批量请求 br.operations(op -> op .index(idx -> idx  .index(vo.getIndexName())  .id(docId)  .document(vo) ) ); } // 执行批量操作 BulkResponse response = client.bulk(br.build()); if (response.errors()) { log.error(\"批量操作存在错误:{}\", response.errors()); // 处理错误详情 for (BulkResponseItem item : response.items()) { if (item.error() != null) {  log.error(\"文档ID={} 操作失败:{}\", item.id(), item.error().reason()); } } } else { log.info(\"批量操作成功,处理文档数:{}\", voList.size()); } } catch (IOException e) { log.error(\"批量操作失败\", e); }}
4.3.3 删除文档
/** * 删除指定文档 */public void deleteDocument(Long id, String businessType, String indexName) { try { String docId = id + businessType; DeleteResponse response = client.delete(d -> d .index(indexName) .id(docId) ); log.info(\"文档删除成功:索引={}, ID={}\", response.index(), docId); } catch (IOException e) { log.error(\"删除文档失败\", e); }}

五、进阶与最佳实践

5.1 异步操作与性能优化

  • 异步 API:使用client.searchAsync()等异步方法,避免阻塞主线程:

    // 异步查询示例client.searchAsync( s -> s.index(\"articles\").query(q -> q.matchAll(m -> m)), ArticleSearchVo.class, new ActionListener() { @Override public void onResponse(SearchResponse response) { // 处理成功响应 } @Override public void onFailure(Exception e) { // 处理异常 } });
  • 连接池配置:调整RestClient的连接池大小(默认 50),适应高并发场景:

    httpClientBuilder.setMaxConnTotal(200); // 最大连接数httpClientBuilder.setMaxConnPerRoute(50); // 每个路由最大连接数

5.2 异常处理与重试机制

  • 捕获ElasticsearchExceptionIOException等异常,区分网络错误与业务错误。
  • 使用 Spring Retry 或 Guava Retrying 实现失败重试(针对 transient 错误):
    @Retryable(value = IOException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))public void retryableOperation() throws IOException { // 可能失败的ES操作}

5.3 索引设计建议

  • 合理设置 Mapping:区分text(分词)与keyword(精确匹配)类型,设置合适的分词器(如 IK 分词器用于中文)。
  • 索引生命周期管理:使用 ILM(Index Lifecycle Management)自动管理索引的创建、滚动、删除。
  • 避免过度查询:通过_source过滤返回字段,减少网络传输:
    .source(s -> s.filter(f -> f.includes(\"title\", \"author\"))) // 只返回指定字段

六、总结

Elasticsearch 8 + 的 Java 客户端通过流式 API 大幅简化了开发流程,结合 Spring 框架可快速实现高效的全文检索功能。本文从环境搭建、客户端配置、核心操作到进阶实践,覆盖了 Java 操作 ES 的关键知识点。

在实际开发中,需根据业务场景选择合适的查询方式,注重性能优化与异常处理,并通过批量操作、异步 API 等手段提升系统吞吐量。掌握这些技能后,可轻松应对日志分析、商品搜索、内容推荐等各类 ES 应用场景。