Elasticsearch Java 客户端(9.x)


一、概述

1. 简述

完成 Elasticsearch 9.x 官方 Java 客户端的安装、实例化,并演示如何通过客户端执行核心操作。


2. 环境要求

  • Java 17 或更高版本

🔍 深度分析:为什么必须是 Java 17?

  1. LTS 版本稳定性:Java 17 是长期支持版本,生产环境兼容性与安全性有保障。

  2. 新特性依赖 :客户端内部使用了 Java 17 的记录类(Record)模式匹配等特性,简化代码并提升性能。

  3. 底层依赖约束 :客户端依赖的 jakarta.json 等库仅支持 Java 17+。


3. 安装

3.1 Gradle 项目

Groovy 复制代码
dependencies {
    implementation 'co.elastic.clients:elasticsearch-java:9.3.0'
}

3.2 Maven 项目

XML 复制代码
<dependencies>
    <dependency>
        <groupId>co.elastic.clients</groupId>
        <artifactId>elasticsearch-java</artifactId>
        <version>9.3.0</version>
    </dependency>
</dependencies>

🔧 补充

  1. 版本对齐 :客户端版本需与 Elasticsearch 服务端大版本一致(如服务端是 9.4.x,客户端也用 9.4.x)。

  2. Maven 仓库配置:若中央仓库下载失败,添加 Elastic 官方仓库:

    XML 复制代码
    <repositories>
        <repository>
            <id>elastic-co</id>
            <url>https://artifacts.elastic.co/maven</url>
        </repository>
    </repositories>

4. 连接客户端

连接到 Elasticsearch

4.1 基础连接示例

Java 复制代码
// 1. 配置连接信息
String serverUrl = "https://localhost:9200";
String apiKey = "VnVhQ2ZHY0JDZGJrU...";

// 2. 创建客户端
ElasticsearchClient esClient = ElasticsearchClient.of(b -> b
    .host(serverUrl)
    .apiKey(apiKey)
);

// 3. 使用客户端...

// 4. 关闭资源
esClient.close();

🔧 生产级连接优化

(1)HTTPS 与自签名证书处理

Elasticsearch 9.x 默认启用 HTTPS,开发环境需配置信任自签名证书:

Java 复制代码
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.ssl.SSLContextBuilder;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;

// 1. 构建 SSL 上下文(信任自签名证书,仅用于开发!)
SSLContext sslContext = SSLContextBuilder.create()
    .loadTrustMaterial(new TrustSelfSignedStrategy())
    .build();

// 2. 构建底层 RestClient
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200, "https"))
    .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder
        .setSSLContext(sslContext)
        .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) // 跳过主机名验证
    )
    .build();

// 3. 构建传输层与客户端
ElasticsearchTransport transport = new RestClientTransport(
    restClient, new JacksonJsonpMapper()
);
ElasticsearchClient esClient = new ElasticsearchClient(transport);
(2)连接超时与重试
Java 复制代码
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200, "https"))
    .setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder
        .setConnectTimeout(5000)    // 连接超时:5秒
        .setSocketTimeout(60000)    // 套接字超时:60秒
    )
    .build();
(3)资源管理最佳实践

使用 try-with-resources 自动关闭客户端:

Java 复制代码
try (ElasticsearchClient esClient = new ElasticsearchClient(transport)) {
    // 执行操作
} // 自动关闭,无需手动调用 close()

5. 核心操作

5.1 创建索引

简化版:
Java 复制代码
esClient.indices().create(c -> c.index("products"));
🔧 生产级优化(含 Settings & Mappings)
Java 复制代码
import co.elastic.clients.elasticsearch._types.mapping.*;
import java.util.HashMap;
import java.util.Map;

// 1. 定义字段映射(Mappings)
Map<String, Property> properties = new HashMap<>();
properties.put("sku", Property.of(p -> p.keyword(KeywordProperty.of(k -> k)))); // 精确匹配
properties.put("name", Property.of(p -> p.text(TextProperty.of(t -> t))));       // 全文搜索
properties.put("price", Property.of(p -> p.double_(DoubleProperty.of(d -> d)))); // 数字类型

// 2. 创建索引(含 Settings)
esClient.indices().create(c -> c
    .index("products")
    .settings(s -> s
        .numberOfShards("3")    // 3个主分片
        .numberOfReplicas("1")  // 1个副本分片
    )
    .mappings(m -> m.properties(properties))
);
🔍 深度分析
  • 主分片数:创建后不可修改,需根据数据量提前规划(建议单分片数据量 < 50GB)。

  • 副本分片:可动态调整,用于高可用(生产环境建议至少 1 个副本)。


5.2 索引文档

简化版:
Java 复制代码
Product product = new Product("bk-1", "City bike", 123.0);
esClient.index(i -> i.index("products").id(product.getSku()).document(product));
🔧 优化与补充
(1)Product 类完整定义
Java 复制代码
public class Product {
    private String sku;
    private String name;
    private Double price;

    // 必须有无参构造函数(用于 JSON 反序列化)
    public Product() {}

    public Product(String sku, String name, Double price) {
        this.sku = sku;
        this.name = name;
        this.price = price;
    }

    // Getter & Setter
    public String getSku() { return sku; }
    public void setSku(String sku) { this.sku = sku; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Double getPrice() { return price; }
    public void setPrice(Double price) { this.price = price; }
}
(2)带参数的索引操作
Java 复制代码
import co.elastic.clients.elasticsearch.core.IndexResponse;
import co.elastic.clients.elasticsearch._types.Refresh;

Product product = new Product("bk-1", "City bike", 123.0);

IndexResponse response = esClient.index(i -> i
    .index("products")
    .id(product.getSku())
    .document(product)
    .refresh(Refresh.WaitFor) // 等待刷新后返回(保证搜索可见性)
);

System.out.println("文档版本:" + response.version()); // 乐观锁版本号

5.3 获取文档

简化版:
Java 复制代码
GetResponse<Product> response = esClient.get(g -> g.index("products").id("bk-1"), Product.class);
🔧 优化:字段过滤与路由
Java 复制代码
GetResponse<Product> response = esClient.get(g -> g
    .index("products")
    .id("bk-1")
    .source(s -> s.filter(f -> f.includes("name", "price"))) // 只返回指定字段
    .routing("custom-routing") // 若索引时使用了路由,获取时需指定
, Product.class);

if (response.found()) {
    Product product = response.source();
    System.out.println("产品名称:" + product.getName());
}

5.4 搜索文档

简化版:
Java 复制代码
SearchResponse<Product> response = esClient.search(s -> s
    .index("products")
    .query(q -> q.match(t -> t.field("name").query("bike")))
, Product.class);
🔧 生产级搜索(组合查询 + 分页 + 高亮)
Java 复制代码
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch.core.search.Hit;

// 1. 构建组合查询(Bool Query)
Query matchQuery = Query.of(q -> q.match(m -> m.field("name").query("bike")));
Query rangeQuery = Query.of(q -> q.range(r -> r.field("price").gte(JsonData.of(100)).lte(JsonData.of(200))));

Query boolQuery = Query.of(q -> q.bool(BoolQuery.of(b -> b
    .must(matchQuery)   // 必须匹配
    .filter(rangeQuery) // 过滤条件(不影响评分)
)));

// 2. 执行搜索
SearchResponse<Product> response = esClient.search(s -> s
    .index("products")
    .query(boolQuery)
    .from(0)          // 分页起始位置
    .size(10)         // 每页大小
    .sort(sort -> sort.field(f -> f.field("price").order(SortOrder.Asc))) // 按价格排序
    .highlight(h -> h.fields("name", f -> f)) // 高亮匹配字段
, Product.class);

// 3. 解析结果
System.out.println("命中总数:" + response.hits().total().value());
for (Hit<Product> hit : response.hits().hits()) {
    Product product = hit.source();
    System.out.println("产品:" + product.getName() + ",价格:" + product.getPrice());
    // 高亮结果
    if (hit.highlight() != null) {
        System.out.println("高亮名称:" + hit.highlight().get("name").get(0));
    }
}

5.5 更新文档

简化版:
Java 复制代码
esClient.update(u -> u.index("products").id("bk-1").upsert(product), Product.class);
🔧 优化:部分更新与脚本更新
(1)部分更新(仅修改指定字段)
Java 复制代码
Map<String, Object> updateFields = new HashMap<>();
updateFields.put("price", 150.0); // 更新价格
updateFields.put("description", "A comfortable city bike"); // 新增字段

esClient.update(u -> u
    .index("products")
    .id("bk-1")
    .doc(updateFields) // 部分更新
, Product.class);
(2)脚本更新(复杂逻辑)
Java 复制代码
String script = "ctx._source.price *= 1.1"; // 价格上涨 10%

esClient.update(u -> u
    .index("products")
    .id("bk-1")
    .script(s -> s.inline(i -> i.lang("painless").source(script)))
, Product.class);

5.6 删除文档与索引

简化版:
Java 复制代码
esClient.delete(d -> d.index("products").id("bk-1"));
esClient.indices().delete(d -> d.index("products"));
🔧 优化:安全删除
Java 复制代码
// 删除文档(带结果判断)
DeleteResponse deleteResponse = esClient.delete(d -> d.index("products").id("bk-1"));
System.out.println("删除结果:" + deleteResponse.result()); // "Deleted" 或 "Not_Found"

// 删除索引(先判断是否存在)
String indexName = "products";
if (esClient.indices().exists(e -> e.index(indexName)).value()) {
    esClient.indices().delete(d -> d.index(indexName));
    System.out.println("索引已删除");
}

二、最佳实践总结

  1. 异常处理 :捕获 ElasticsearchExceptionIOException

  2. 异步客户端 :高并发场景使用 ElasticsearchAsyncClient

  3. 连接池配置 :调整 MaxConnTotalMaxConnPerRoute 提升性能。

  4. 日志调试 :配置 co.elastic.clients 日志级别为 DEBUG

相关推荐
重生之后端学习2 小时前
17. 电话号码的字母组合
java·开发语言·数据结构·算法·深度优先
蜜獾云2 小时前
JAVA面试题速记-redis知识点
java·开发语言·redis
Drifter_yh2 小时前
「JVM」 深入理解 StringTable:从底层编译优化到 intern 核心解密
java·jvm
海兰2 小时前
ES_QL 稠密向量检索:本地部署实操
大数据·elasticsearch·搜索引擎
JavaLearnerZGQ2 小时前
Spring Boot 流式响应接口核心组件解析
java·spring boot·后端
cur1es2 小时前
【TCP 协议的相关特性】
java·网络·网络协议·tcp/ip·tcp·滑动窗口·连接管理
山岚的运维笔记2 小时前
SQL Server笔记 -- 第80章:分页
java·数据库·笔记·sql·microsoft·sqlserver
侧耳倾听1112 小时前
kibana-基本使用
elasticsearch
开开心心就好2 小时前
文字转语音无字数限,对接微软接口比付费爽
java·linux·开发语言·人工智能·pdf·语音识别