基于 Elasticsearch 8.19.0 官方 Java API 客户端
Elasticsearch 学习笔记
基于 Elasticsearch 8.19.0 官方 Java API 客户端,涵盖依赖配置、客户端初始化、CRUD 操作及查询示例。
一、技术选型与版本
| 组件 | 版本 | 说明 |
|---|---|---|
| Elasticsearch Server | 8.x | 目标 ES 服务端版本 |
elasticsearch-java |
8.19.0 | 官方 Java API 客户端(类型安全、Fluent DSL) |
elasticsearch-rest-client |
8.19.0 | 底层 HTTP 传输层 |
jakarta.json-api |
2.1.3 | JSON-P API 规范 |
parsson |
1.1.7 | JSON-P 实现(运行时) |
为什么不用 Spring Data Elasticsearch?
直接使用 ES 官方 co.elastic.clients Java API 客户端,而非 spring-boot-starter-data-elasticsearch,原因:
- 更灵活,不依赖 Spring Data 的
@Document、@Field注解体系 - 使用官方最新的 Fluent DSL 风格,代码可读性高
- 避免 Spring Data 封装层的版本滞后问题
二、Maven 依赖
<!-- Elasticsearch Java Client(官方高级API) -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.19.0</version>
</dependency>
<!-- ES底层REST客户端 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>8.19.0</version>
</dependency>
<!-- JSON-P API(JSON处理规范) -->
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.1.3</version>
</dependency>
<!-- JSON-P 实现(运行时) -->
<dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
<version>1.1.7</version>
</dependency>
依赖关系说明:
elasticsearch-java (高级API,Fluent DSL)
└── elasticsearch-rest-client (底层HTTP通信)
└── apache httpclient (HTTP连接管理、认证)
└── jakarta.json-api (JSON序列化/反序列化规范)
└── parsson (JSON-P的具体实现)
三、ES 连接配置
3.1 YAML 配置
es:
host: localhost
port: 9200
username: elastic
password: 123456
注意: 如果使用 Spring Boot 多环境配置(dev / test / prod),每个环境的 yaml 文件都需要包含对应的 ES 配置,否则启动时会因
@Value注入失败而报错。
3.2 Java 配置类
@Configuration
public class ElasticsearchConfig {
@Value("${es.host}")
private String host;
@Value("${es.port}")
private Integer port;
@Value("${es.username}")
private String userName;
@Value("${es.password}")
private String passWord;
@Bean
public ElasticsearchClient elasticsearchClient() {
// 1. 构建认证凭证
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(userName, passWord)
);
// 2. 构建底层 REST 客户端
RestClient restClient = RestClient.builder(new HttpHost(host, port))
.setHttpClientConfigCallback(httpClient ->
httpClient.setDefaultCredentialsProvider(credentialsProvider)
)
.build();
// 3. 包装为 Transport 层(指定 JSON 映射器)
RestClientTransport transport = new RestClientTransport(
restClient,
new JacksonJsonpMapper()
);
// 4. 创建高级客户端
return new ElasticsearchClient(transport);
}
}
组件层级结构:
ElasticsearchClient ← 业务代码直接使用,提供 Fluent DSL
└── RestClientTransport ← 传输抽象层,负责请求/响应序列化
├── RestClient ← 底层 HTTP 客户端(连接池、认证)
└── JacksonJsonpMapper ← JSON ↔ Java 对象的映射器
关键点:
BasicCredentialsProvider+AuthScope.ANY实现全局 HTTP Basic 认证JacksonJsonpMapper利用 Jackson 生态进行 JSON 处理ElasticsearchClient作为 Spring Bean 注入,线程安全,全局复用
四、文档模型
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserPortrait {
private String name; // 用户名
private Integer age; // 年龄
private String portrait; // 用户画像描述(全文搜索字段)
}
说明:
- 纯 POJO,不使用
@Document、@Field、@Id等注解 - JSON 序列化/反序列化由
JacksonJsonpMapper自动完成 - 索引名称和文档 ID 在代码中显式指定,而非通过注解声明
- 文档类必须有无参构造器 + getter/setter,或使用 Lombok
@Data+@NoArgsConstructor
五、核心 API 操作
5.1 新增文档
CreateResponse createResponse = elasticsearchClient.create(
client -> client
.index("user_portrait_index") // 指定索引名
.id("9") // 文档ID
.document(userPortrait) // 文档对象
);
对应 ES REST API: PUT /{index}/_create/{id}
注意事项:
create()对应 ES 的_create端点,若文档已存在则报错- 如需 upsert 语义(存在则更新),应使用
index()
请求示例:
{
"name": "张三",
"age": 25,
"portrait": "Java开发工程师,熟悉微服务架构"
}
5.2 按 ID 查询
GetResponse<UserPortrait> response = elasticsearchClient.get(
client -> client
.index("user_portrait_index")
.id(id),
UserPortrait.class // 指定反序列化目标类型
);
UserPortrait result = response.source();
对应 ES REST API: GET /{index}/_doc/{id}
注意事项:
- 若文档不存在,
response.source()返回null - 第二个参数
.class决定了_source反序列化的目标类型
5.3 全文搜索
SearchResponse<UserPortrait> response = elasticsearchClient.search(
client -> client
.size(10) // 返回最多10条
.query(q -> q.bool(b -> b
// must子句:全文匹配 portrait 字段
.must(m -> m.match(mm -> mm
.field("portrait")
.query(keyword)
))
// filter子句:年龄 >= 10
.filter(f -> f.range(r -> r
.number(n -> n
.field("age")
.gte(10.0)
)
))
)),
UserPortrait.class
);
List<UserPortrait> results = response.hits().hits().stream()
.map(Hit::source)
.collect(Collectors.toList());
对应 ES REST API: POST /{index}/_search
等效的 ES REST 请求体:
{
"size": 10,
"query": {
"bool": {
"must": [
{ "match": { "portrait": "Java微服务" } }
],
"filter": [
{ "range": { "age": { "gte": 10 } } }
]
}
}
}
查询解析:
match查询对portrait字段做全文分词检索,参与相关性评分range过滤age >= 10的文档,不参与评分,可被 ES 缓存,性能更好size(10)限制返回最多 10 条结果
六、Fluent DSL 核心模式
ES Java 8.x 客户端使用 Lambda 表达式构建请求,核心模式如下:
elasticsearchClient.<操作类型>(client -> client
.<参数1>(p1 -> p1
.<子参数>(p2 -> p2
.<具体设置>()
)
),
<返回类型>.class
);
常用操作速查
| 操作 | 方法 | ES REST API | 用途 |
|---|---|---|---|
| 创建文档 | elasticsearchClient.create() |
PUT /{index}/_create/{id} |
新建文档(已存在则报错) |
| 索引文档 | elasticsearchClient.index() |
PUT /{index}/_doc/{id} |
新建或替换文档(upsert) |
| 获取文档 | elasticsearchClient.get() |
GET /{index}/_doc/{id} |
按 ID 获取 |
| 搜索 | elasticsearchClient.search() |
POST /{index}/_search |
全文搜索、聚合查询 |
| 更新文档 | elasticsearchClient.update() |
POST /{index}/_update/{id} |
部分更新 |
| 删除文档 | elasticsearchClient.delete() |
DELETE /{index}/_doc/{id} |
按 ID 删除 |
Bool 查询组合
q.bool(b -> b
.must(...) // 必须匹配,参与评分
.filter(...) // 必须匹配,不参与评分(缓存友好)
.should(...) // 可选匹配,提高相关性
.mustNot(...) // 必须不匹配
)
must vs filter 的区别:
must:参与相关性评分计算,影响_score排序filter:只做过滤(是/否),不计算评分,可利用缓存,性能更好
七、ES 连接参数说明
| 参数 | YAML 路径 | 说明 |
|---|---|---|
| 主机地址 | es.host |
ES 服务地址,如 localhost |
| 端口 | es.port |
ES HTTP 端口,默认 9200 |
| 用户名 | es.username |
ES 认证用户名 |
| 密码 | es.password |
ES 认证密码 |
八、常见问题
8.1 启动报错 Could not resolve placeholder 'es.host'
某个 Spring profile 的 yaml 文件中缺少 ES 配置,需在对应的 yaml 文件中补全。
8.2 文档 ID 策略
- 使用业务主键(如用户 ID)作为文档 ID,便于按 ID 更新
- 调用
elasticsearchClient.index()不指定 ID,让 ES 自动生成 UUID
8.3 create() vs index() 的区别
| 方法 | 文档已存在时 | 文档不存在时 |
|---|---|---|
create() |
报错(409 Conflict) | 创建新文档 |
index() |
替换现有文档 | 创建新文档 |
8.4 结果集解析
SearchResponse<T> response = ...;
// 命中的文档列表
List<Hit<T>> hits = response.hits().hits();
// 提取 _source 对象
List<T> sources = hits.stream().map(Hit::source).collect(Collectors.toList());
// 命中总数(注意 ES 7.x+ 默认最多统计 10000)
long total = response.hits().total().value();
// 最高相关性评分
double maxScore = response.hits().maxScore();
九、扩展阅读
- Elasticsearch Java API Client 官方文档
- ES Bool Query 详解
- ES Match Query 详解# Elasticsearch 学习笔记
基于 Elasticsearch 8.19.0 官方 Java API 客户端,涵盖依赖配置、客户端初始化、CRUD 操作及查询示例。
一、技术选型与版本
| 组件 | 版本 | 说明 |
|---|---|---|
| Elasticsearch Server | 8.x | 目标 ES 服务端版本 |
elasticsearch-java |
8.19.0 | 官方 Java API 客户端(类型安全、Fluent DSL) |
elasticsearch-rest-client |
8.19.0 | 底层 HTTP 传输层 |
jakarta.json-api |
2.1.3 | JSON-P API 规范 |
parsson |
1.1.7 | JSON-P 实现(运行时) |
为什么不用 Spring Data Elasticsearch?
直接使用 ES 官方 co.elastic.clients Java API 客户端,而非 spring-boot-starter-data-elasticsearch,原因:
- 更灵活,不依赖 Spring Data 的
@Document、@Field注解体系 - 使用官方最新的 Fluent DSL 风格,代码可读性高
- 避免 Spring Data 封装层的版本滞后问题
二、Maven 依赖
<!-- Elasticsearch Java Client(官方高级API) -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.19.0</version>
</dependency>
<!-- ES底层REST客户端 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>8.19.0</version>
</dependency>
<!-- JSON-P API(JSON处理规范) -->
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.1.3</version>
</dependency>
<!-- JSON-P 实现(运行时) -->
<dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
<version>1.1.7</version>
</dependency>
依赖关系说明:
elasticsearch-java (高级API,Fluent DSL)
└── elasticsearch-rest-client (底层HTTP通信)
└── apache httpclient (HTTP连接管理、认证)
└── jakarta.json-api (JSON序列化/反序列化规范)
└── parsson (JSON-P的具体实现)
三、ES 连接配置
3.1 YAML 配置
es:
host: localhost
port: 9200
username: elastic
password: 123456
注意: 如果使用 Spring Boot 多环境配置(dev / test / prod),每个环境的 yaml 文件都需要包含对应的 ES 配置,否则启动时会因
@Value注入失败而报错。
3.2 Java 配置类
@Configuration
public class ElasticsearchConfig {
@Value("${es.host}")
private String host;
@Value("${es.port}")
private Integer port;
@Value("${es.username}")
private String userName;
@Value("${es.password}")
private String passWord;
@Bean
public ElasticsearchClient elasticsearchClient() {
// 1. 构建认证凭证
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(userName, passWord)
);
// 2. 构建底层 REST 客户端
RestClient restClient = RestClient.builder(new HttpHost(host, port))
.setHttpClientConfigCallback(httpClient ->
httpClient.setDefaultCredentialsProvider(credentialsProvider)
)
.build();
// 3. 包装为 Transport 层(指定 JSON 映射器)
RestClientTransport transport = new RestClientTransport(
restClient,
new JacksonJsonpMapper()
);
// 4. 创建高级客户端
return new ElasticsearchClient(transport);
}
}
组件层级结构:
ElasticsearchClient ← 业务代码直接使用,提供 Fluent DSL
└── RestClientTransport ← 传输抽象层,负责请求/响应序列化
├── RestClient ← 底层 HTTP 客户端(连接池、认证)
└── JacksonJsonpMapper ← JSON ↔ Java 对象的映射器
关键点:
BasicCredentialsProvider+AuthScope.ANY实现全局 HTTP Basic 认证JacksonJsonpMapper利用 Jackson 生态进行 JSON 处理ElasticsearchClient作为 Spring Bean 注入,线程安全,全局复用
四、文档模型
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserPortrait {
private String name; // 用户名
private Integer age; // 年龄
private String portrait; // 用户画像描述(全文搜索字段)
}
说明:
- 纯 POJO,不使用
@Document、@Field、@Id等注解 - JSON 序列化/反序列化由
JacksonJsonpMapper自动完成 - 索引名称和文档 ID 在代码中显式指定,而非通过注解声明
- 文档类必须有无参构造器 + getter/setter,或使用 Lombok
@Data+@NoArgsConstructor
五、核心 API 操作
5.1 新增文档
CreateResponse createResponse = elasticsearchClient.create(
client -> client
.index("user_portrait_index") // 指定索引名
.id("9") // 文档ID
.document(userPortrait) // 文档对象
);
对应 ES REST API: PUT /{index}/_create/{id}
注意事项:
create()对应 ES 的_create端点,若文档已存在则报错- 如需 upsert 语义(存在则更新),应使用
index()
请求示例:
{
"name": "张三",
"age": 25,
"portrait": "Java开发工程师,熟悉微服务架构"
}
5.2 按 ID 查询
GetResponse<UserPortrait> response = elasticsearchClient.get(
client -> client
.index("user_portrait_index")
.id(id),
UserPortrait.class // 指定反序列化目标类型
);
UserPortrait result = response.source();
对应 ES REST API: GET /{index}/_doc/{id}
注意事项:
- 若文档不存在,
response.source()返回null - 第二个参数
.class决定了_source反序列化的目标类型
5.3 全文搜索
SearchResponse<UserPortrait> response = elasticsearchClient.search(
client -> client
.size(10) // 返回最多10条
.query(q -> q.bool(b -> b
// must子句:全文匹配 portrait 字段
.must(m -> m.match(mm -> mm
.field("portrait")
.query(keyword)
))
// filter子句:年龄 >= 10
.filter(f -> f.range(r -> r
.number(n -> n
.field("age")
.gte(10.0)
)
))
)),
UserPortrait.class
);
List<UserPortrait> results = response.hits().hits().stream()
.map(Hit::source)
.collect(Collectors.toList());
对应 ES REST API: POST /{index}/_search
等效的 ES REST 请求体:
{
"size": 10,
"query": {
"bool": {
"must": [
{ "match": { "portrait": "Java微服务" } }
],
"filter": [
{ "range": { "age": { "gte": 10 } } }
]
}
}
}
查询解析:
match查询对portrait字段做全文分词检索,参与相关性评分range过滤age >= 10的文档,不参与评分,可被 ES 缓存,性能更好size(10)限制返回最多 10 条结果
六、Fluent DSL 核心模式
ES Java 8.x 客户端使用 Lambda 表达式构建请求,核心模式如下:
elasticsearchClient.<操作类型>(client -> client
.<参数1>(p1 -> p1
.<子参数>(p2 -> p2
.<具体设置>()
)
),
<返回类型>.class
);
常用操作速查
| 操作 | 方法 | ES REST API | 用途 |
|---|---|---|---|
| 创建文档 | elasticsearchClient.create() |
PUT /{index}/_create/{id} |
新建文档(已存在则报错) |
| 索引文档 | elasticsearchClient.index() |
PUT /{index}/_doc/{id} |
新建或替换文档(upsert) |
| 获取文档 | elasticsearchClient.get() |
GET /{index}/_doc/{id} |
按 ID 获取 |
| 搜索 | elasticsearchClient.search() |
POST /{index}/_search |
全文搜索、聚合查询 |
| 更新文档 | elasticsearchClient.update() |
POST /{index}/_update/{id} |
部分更新 |
| 删除文档 | elasticsearchClient.delete() |
DELETE /{index}/_doc/{id} |
按 ID 删除 |
Bool 查询组合
q.bool(b -> b
.must(...) // 必须匹配,参与评分
.filter(...) // 必须匹配,不参与评分(缓存友好)
.should(...) // 可选匹配,提高相关性
.mustNot(...) // 必须不匹配
)
must vs filter 的区别:
must:参与相关性评分计算,影响_score排序filter:只做过滤(是/否),不计算评分,可利用缓存,性能更好
七、ES 连接参数说明
| 参数 | YAML 路径 | 说明 |
|---|---|---|
| 主机地址 | es.host |
ES 服务地址,如 localhost |
| 端口 | es.port |
ES HTTP 端口,默认 9200 |
| 用户名 | es.username |
ES 认证用户名 |
| 密码 | es.password |
ES 认证密码 |
八、常见问题
8.1 启动报错 Could not resolve placeholder 'es.host'
某个 Spring profile 的 yaml 文件中缺少 ES 配置,需在对应的 yaml 文件中补全。
8.2 文档 ID 策略
- 使用业务主键(如用户 ID)作为文档 ID,便于按 ID 更新
- 调用
elasticsearchClient.index()不指定 ID,让 ES 自动生成 UUID
8.3 create() vs index() 的区别
| 方法 | 文档已存在时 | 文档不存在时 |
|---|---|---|
create() |
报错(409 Conflict) | 创建新文档 |
index() |
替换现有文档 | 创建新文档 |
8.4 结果集解析
SearchResponse<T> response = ...;
// 命中的文档列表
List<Hit<T>> hits = response.hits().hits();
// 提取 _source 对象
List<T> sources = hits.stream().map(Hit::source).collect(Collectors.toList());
// 命中总数(注意 ES 7.x+ 默认最多统计 10000)
long total = response.hits().total().value();
// 最高相关性评分
double maxScore = response.hits().maxScore();
九、扩展阅读
更多推荐
所有评论(0)