Elasticsearch 8.x 集成与 Java API 使用指南
目录
背景
版本区别
安装elaticsearch8.x服务
启动es服务
安装es管理平台
项目集成
pom.xml文件引入依赖
application.yml配置
ES初始化配置类实现
ES8.x常用API实现
1.判断es索引是否存在
2.删除索引
3.创建索引
4.新增文档
5.更新文档
6.根据id查询文档
7.根据id删除文档
8.查询文档列表
定义接口创建索引
定义业务方法
定义请求接口
操作接口
背景
随着 Elasticsearch 8.x 的发布,公司决定将现有的 Elasticsearch 7.x 版本升级到 8.x。然而,在升级过程中,我们发现许多 API 和功能发生了不兼容的变化,导致系统在迁移过程中遇到了大量问题。虽然官方文档提供了基本的操作指南,但实际应用中涉及的细节和调整却并未得到充分覆盖。为了帮助大家更顺利地过渡到 8.x,并有效应对这些变化, 本文将详细探讨 Elasticsearch 8.x 与 7.x 版本之间的主要差异,特别是在 Java 开发中的实际应用与迁移问题。
版本区别
依赖差异
- 版本7.x以及更早之前版本依赖
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.x.x</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.x.x</version>
</dependency>
- 8.x版本依赖
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.x.x</version>
</dependency>
安全性增强 :与 7.x 的默认无安全配置不同,8.x 默认启用安全功能,包括用户认证、授权、TLS 加密等,给开发者带来了更多的配置和管理任务。
查询和聚合优化:某些查询接口和聚合方法发生了变化,特别是对于大规模数据集的支持和性能提升方面有了显著进步,但这些变化需要开发者重新调整代码实现。
安装elaticsearch8.x服务
本文试例在window系统本地安装服务
启动es服务
elaticsearch版本为:8.16
官方下载地址:Past Releases of Elastic Stack Software | Elastic
百度云网盘:百度网盘 请输入提取码 提取码: 92h6
下载完解压目录如下
进入config目录修改yml文件
因为8.x版本默认开启权限校验以及ssl证书校验,为了方便后续使用,这里只保留权限校验,关闭ssl证书校验
进入bin目录执行cmd打开命令行重置登录密码
.\elasticsearch-reset-password -u elastic
默认账号:elastic
记得保存好密码,以免遗忘
进入bin目录双击elasticsearch.bat启动es服务
安装es管理平台
- 可自行安装es官方面板:kibana
- 本文使用的是google浏览器插件:Multi Elasticsearch Heads(可在google插件市场下载)
打开插件并连接es服务
连接成功
项目集成
这里通过集成在spring boot项目中来了解es8.x的配置连接以及api的使用
注:虽然基于springboot,但是我们这里不使用spring boot data提供的es集成依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>x.x.x</version>
</dependency>
因为data只提供了一些es简单操作的api,对于复杂的只有通过script手写DSL语句来完成,扩展性和维护性都不是很好,对不熟悉DSL语句的也很不友好,所以我们还是选择使用原生依赖 elasticsearch-java
可下载博主github源码参考:GitHub - RemainderTime/spring-boot-base-demo: 拿来即用springboot基础框架项目
pom.xml文件引入依赖
<!-- elasticsearch8.x 搜索引擎 -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.16.0</version>
</dependency>
application.yml配置
elasticsearch:
host: localhost
port: 9200
username: elastic
password: 8wVPrsP=9vlQWHBuHniH #window系统本地启动 es8.x 重置密码命令:.\elasticsearch-reset-password -u elastic
ES初始化配置类实现
@Component
public class EsConfig {
@Value("${elasticsearch.host}")
private String elasticsearchHost;
@Value("${elasticsearch.port}")
private int elasticsearchPort;
@Value("${elasticsearch.username}")
private String username;
@Value("${elasticsearch.password}")
private String password;
/**
-最大连接数 (maxConnTotal):设置总的最大连接数,取决于业务的并发量。500-2000 之间较为合理。
-每个节点的最大连接数 (maxConnPerRoute):控制每个节点的最大连接数,建议 50-100 之间。
-IO 线程数 (setIoThreadCount):根据 CPU 核心数设置,通常为 2-4 倍 CPU 核心数。
-连接超时、套接字超时、获取连接超时:一般设置为 10-30 秒,复杂查询或大数据量操作可适当增加到 20-60 秒。
-失败监听器 (setFailureListener):自定义重试和故障处理逻辑,确保高可用性。
*/
@Bean
public ElasticsearchClient elasticsearchClient() {
// 创建凭证提供者
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(username, password)
);
// 自定义 RestClientBuilder 配置
RestClientBuilder restClientBuilder = RestClient.builder(
new HttpHost(elasticsearchHost, elasticsearchPort, "http")
).setHttpClientConfigCallback(httpClientBuilder ->
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider) // 配置认证信息
);
// 配置连接超时、套接字超时、获取连接超时
restClientBuilder.setRequestConfigCallback(builder ->
builder.setConnectTimeout(20000)
.setSocketTimeout(20000)
.setConnectionRequestTimeout(20000)
);
// 创建 RestClientTransport 和 ElasticsearchClient
RestClient restClient = restClientBuilder.build();
ElasticsearchTransport transport = new RestClientTransport(
restClient,
new JacksonJsonpMapper() // 使用 Jackson 进行 JSON 处理
);
return new ElasticsearchClient(transport);
}
}
ES8.x常用API实现
先创建一个全局es工具类
@Slf4j
@Component
public class EsUtil {
public static ElasticsearchClient esClient;
{
esClient = (ElasticsearchClient) ApplicationContextUtils.getBean("elasticsearchClient");
}
/...
}
下面罗列工具类中实现的常用的操作方法。
1.判断es索引是否存在
public static boolean existIndex(String indexName) {
try {
// 创建 ExistsRequest 请求
ExistsRequest request = new ExistsRequest.Builder()
.index(indexName)
.build();
// 发送请求并获取响应
BooleanResponse response = esClient.indices().exists(request);
// 返回索引是否存在
return response.value();
} catch (Exception e) {
// 处理异常
e.printStackTrace();
return false;
}
}
2.删除索引
@SneakyThrows
public static void delIndex(String indexName) {
if (existIndex(indexName)) {
return;
}
esClient.indices().delete(d -> d.index(indexName));
}
3.创建索引
public static void createIndex(String indexName) {
if (existIndex(indexName)) {
throw new RuntimeException("索引已经存在");
}
try {
CreateIndexResponse createIndexResponse = esClient.indices().create(c -> c.index(indexName));
// 处理响应
if (createIndexResponse.acknowledged()) {
log.info(" indexed create successfully.");
} else {
log.info("Failed to create index.");
}
} catch (Exception e) {
// 捕获异常并打印详细错误信息
e.printStackTrace();
throw new RuntimeException("创建索引失败,索引名:" + indexName + ",错误信息:" + e.getMessage(), e);
}
}
4.新增文档
public static boolean addDocument(EsBaseModel esBaseModel) {
try {
// 创建 IndexRequest 实例
IndexRequest request = new IndexRequest.Builder()
.index(esBaseModel.getIndexName())
.id(esBaseModel.getDocumentId()) //指定文档id,不指定会自动生成
.document(esBaseModel.getDocumentModel())
.opType(OpType.Create) // 只会在文档 ID 不存在时创建文档
.build();
IndexResponse response = esClient.index(request);
if ("created".equals(response.result())) {
log.info("Document created: " + response.id());
return true;
} else {
log.info("Document already exists or failed to create.");
return false;
}
} catch (Exception e) {
log.error("es新增文档失败", e);
e.printStackTrace();
}
return false;
}
5.更新文档
public boolean updateDocument(EsBaseModel esBaseModel) {
try {
UpdateRequest updateRequest = new UpdateRequest.Builder<>()
.index(esBaseModel.getIndexName())
.id(esBaseModel.getDocumentId())
.doc(esBaseModel.getDocumentModel()).build();
UpdateResponse updateResponse = esClient.update(updateRequest, esBaseModel.getClazz());
log.info("Document updated: " + updateResponse.id());
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
6.根据id查询文档
public static <T> T getDocumentById(EsBaseModel esBaseModel) {
try {
GetRequest getRequest = new GetRequest.Builder()
.index(esBaseModel.getIndexName())
.id(esBaseModel.getDocumentId())
.build();
GetResponse<T> getResponse = esClient.get(getRequest, esBaseModel.getClazz());
if (getResponse.found()) {
return getResponse.source();
}
} catch (Exception e) {
log.error("es列表查询失败", e);
}
return null;
}
7.根据id删除文档
public static Boolean deleteDocumentById(EsBaseModel esBaseModel) {
try {
DeleteRequest deleteRequest = new DeleteRequest.Builder()
.index(esBaseModel.getDocumentId())
.id(esBaseModel.getDocumentId())
.build();
DeleteResponse deleteResponse = esClient.delete(deleteRequest);
if ("deleted".equals(deleteResponse.result())) {
log.info("Document deleted: " + deleteResponse.id());
return true;
} else {
log.info("Document delete failed: " + deleteResponse.id());
return false;
}
} catch (Exception e) {
log.error("es列表删除失败", e);
}
return false;
}
8.查询文档列表
public static <T> List<T> getDocumentList(EsSearchModel searchModel) {
List<T> eslist = new ArrayList<>();
try {
SearchResponse<T> search = esClient.search(buildSearchRequest(searchModel), searchModel.getClazz());
if (Objects.isNull(search)) {
return eslist;
}
HitsMetadata<T> hits = search.hits();
if (Objects.isNull(hits)) {
return eslist;
}
List<Hit<T>> sourceHitList = hits.hits();
if (CollectionUtils.isEmpty(sourceHitList)) {
return eslist;
}
sourceHitList.forEach(item -> {
// 处理每个命中
eslist.add(item.source());
});
return eslist;
} catch (Exception e) {
log.error("es列表查询失败", e);
}
return eslist;
}
注意!!!由于方法太多,文中就不一一列举,想了解更多方法可下载上面博主提供的github地址进行下载项目并找到EsUtil.class类查看,里面有更多场景的使用方法和api详解
定义接口创建索引
定义业务方法
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public RetObj syncEs(Long userId) {
User user = userMapper.selectById(userId);
if (Objects.isNull(user)) {
return RetObj.error("用户不存在");
}
String index = StringUtil.camelToKebabCase(user.getClass().getSimpleName());
if (!EsUtil.existIndex(index)) {
EsUtil.createIndex(index);
}
EsUtil.addDocument(new EsBaseModel(index, String.valueOf(user.getId()), user, user.getClass()));
return RetObj.success();
}
@Override
public RetObj getEsId(Long userId) {
Object user = EsUtil.getDocumentById(new EsBaseModel("user", String.valueOf(userId), null, User.class));
if(Objects.nonNull(user)){
return RetObj.success(user);
}
return RetObj.error("es中不存在该用户");
}
}
定义请求接口
@RestController(value = "用户控制器")
@RequestMapping("/user")
@Tag(name = "用户控制器")
public class UserController {
@Autowired
private UserService userService;
@Operation(summary = "es同步用户信息", description = "用户信息")
@GetMapping("/syncEs")
public RetObj syncEs(Long userId){
return userService.syncEs(userId);
}
@Operation(summary = "es查询用户信息", description = "用户信息")
@GetMapping("/getEsId")
public RetObj getEsId(Long userId){
return userService.getEsId(userId);
}
}
操作接口
请求接口插入一条数据到索引中
查看面板发现自动创建了一个user索引
查询user索引中的数据存在一条
请求接口查询es数据
至此对于Elaticsearch8.x版本的学习完成了