前言

springboot 整合 ES 有两种方案,ES 官方提供的 Elasticsearch Java API Client 和 spring 提供的 [Spring Data Elasticsearch](Spring Data Elasticsearch)

两种方案各有优劣

Spring:高度封装,用着舒服。缺点是更新不及时,有可能无法使用 ES 的新 API

ES 官方:更新及时,灵活,缺点是太灵活了,基本是一比一复制 REST APIs,项目中使用需要二次封装。

Elasticsearch Java API Client

目前最新版本 ES8.12,要求 jdk8 以上,API 里面使用了大量的 builder 和 lambda

官方也提供了 测试用例

兼容

翻了不少博客,大部分都是使用 High Level Rest Client,这是旧版本的 api,新版本使用 Elasticsearch Java API Client,如何兼容旧版本,官方也提供了解决方案)

下文描述的均是新版 API

添加 jar 包

官方文档:[installation](安装| Elasticsearch Java API 客户端 [8.12] |松紧带 --- Installation | Elasticsearch Java API Client [8.12] | Elastic)

使用的是 maven,在 pom.xml 中添加

<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.12.2</version>
</dependency> <!-- 如果有添加springmvc,此包可不引入 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>

如果报错 ClassNotFoundException: jakarta.json.spi.JsonProvider,则还需要添加

<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>

打印请求

在 application. yml 中添加配置,打印 es 的 http 请求(建议在开发调试时使用)

logging:
level:
tracer: TRACE

连接 ES

配置文件如下,后续所有 ES 操作都通过 ElasticsearchClient 对象

更多配置请看 Common configuration

@Configuration
public class ElasticSearchConfig { @Bean
public ElasticsearchClient esClient() {
// ES服务器URL
String serverUrl = "http://127.0.0.1:9200";
// ES用户名和密码
String userName = "xxx";
String password = "xxx"; BasicCredentialsProvider credsProv = new BasicCredentialsProvider();
credsProv.setCredentials(
AuthScope.ANY, new UsernamePasswordCredentials(userName, password)
); RestClient restClient = RestClient
.builder(HttpHost.create(serverUrl))
.setHttpClientConfigCallback(hc -> hc.setDefaultCredentialsProvider(credsProv))
.build(); ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
return new ElasticsearchClient(transport);
} }

索引操作

代码中的 esClient 就是 ElasticsearchClient,请自行注入 bean

// 索引名字
String indexName = "student"; // 索引是否存在
BooleanResponse books = esClient.indices().exists(e -> e.index(indexName));
System.out.println("索引是否存在:" + books.value()); // 创建索引
esClient.indices().create(c -> c
.index(indexName)
.mappings(mappings -> mappings // 映射
.properties("name", p -> p
.text(t -> t // text类型,index=false
.index(false)
)
)
.properties("age", p -> p
.long_(t -> t) // long类型
)
)
); // 删除索引
esClient.indices().delete(d -> d.index(indexName));

文档操作 (CRUD)

下文以官方测试数据 account. json 为例

实体类

首先定义实体类,用于 ES 中的字段

public class Account {
private String id;
// 解决ES中字段与实体类字段不一致的问题
@JsonProperty("account_number")
private Long account_number;
private String address;
private Integer age;
private Long balance;
private String city;
private String email;
private String employer;
private String firstname;
private String lastname;
private String gender;
private String state;
... 省略get、set方法
}

新增

String indexName = "account";  // 索引名字
Account account = new Account();
account.setId("1");
account.setLastname("guyu"); // 新增
CreateResponse createResponse = esClient.create(c -> c
.index(indexName) // 索引名字
.id(account.getId()) // id
.document(account) // 实体类
);

修改

UpdateResponse<Account> updateResp = esClient.update(u -> u
.index(indexName)
.id(account.getId())
.doc(account),
Account.class
);

删除

DeleteResponse deleteResp = esClient.delete(d -> d.index(indexName).id("1"));

批量新增

批量操作需要使用到 bulk

List<Account> accountList = ...
BulkRequest.Builder br = new BulkRequest.Builder();
for (Account acc : accountList) {
br.operations(op -> op
.create(c -> c
.index(indexName)
.id(acc.getId())
.document(acc)
)
);
}
BulkResponse bulkResp = esClient.bulk(br.build());

有没有觉得批量新增的 .create () 里面的参数很眼熟,批量删除和更新请举一反三

根据 id 查询

// 定义实体类
GetResponse<Account> getResp = esClient.get(g -> g.index(indexName).id("1"), Account.class);
if (getResp.found()) {
Account source = getResp.source(); // 这就是得到的实体类
source.setId(getResp.id());
} // 不定义实体类
GetResponse<ObjectNode> getResp = esClient.get(g -> g
.index(indexName)
.id("1"),
ObjectNode.class
);
if (getResp.found()) {
ObjectNode json = getResp.source();
String firstname = json.get("firstname").asText();
System.out.println(firstname);
}

搜索

搜索全部

SearchResponse<Account> searchResp = esClient.search(s -> s
.index(indexName)
.query(q -> q.matchAll(m -> m)) // 搜索全部
, Account.class
); HitsMetadata<Account> hits = searchResp.hits();
long totalValue = hits.total().value(); // 匹配到的数量
hits.hits().forEach(h -> {
Account acc = h.source(); // 这就是得到的实体类
acc.setId(h.id());
});

ES API 的对象定义,基本与返回的 json 一一对应的,所以 SearchResponse 就不过多赘述。

搜索 firstname = Amber

SearchResponse<Account> searchResp = esClient.search(s -> s
.index(indexName)
.query(q -> q // 查询
.match(t -> t
.field("firstname")
.query("Amber")
)
)
, Account.class
); // 也可以这样写
Query firstNameQuery = MatchQuery.of(m -> m.field("firstname").query("Amber"))._toQuery();
SearchResponse<Account> searchResp = esClient.search(s -> s
.index(indexName)
.query(firstNameQuery)
, Account.class
);

嵌套查询,比如搜索 firstname = Amber AND age = 32

Query firstNameQuery = MatchQuery.of(m -> m.field("firstname").query("Amber"))._toQuery();
Query ageQuery = MatchQuery.of(m -> m.field("age").query(32))._toQuery(); SearchResponse<Account> searchResp = esClient.search(s -> s
.index(indexName)
.query(q -> q
.bool(b -> b.must(firstNameQuery, ageQuery))
)
, Account.class
);

浅分页

from 和 size 参数类似于 mysql 的 limit,详细说明见 Paginate search results

SearchResponse<Account> searchResp = esClient.search(s -> s
.index(indexName)
.from(0) // 分页参数
.size(20) // 分页参数
, Account.class
);

排序

SearchResponse<Account> searchResp = esClient.search(s -> s
.index(indexName)
.sort(so -> so // 排序字段1
.field(f -> f
.field("age")
.order(SortOrder.Asc)
)
)
.sort(so -> so // 排序字段2
.field(f -> f
.field("account_number")
.order(SortOrder.Desc)
)
)
, Account.class
);

Spring Data Elasticsearch

文档: Spring Data Elasticsearch

添加 jar 和配置

pom.xml添加依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

yml 配置

spring:
elasticsearch:
uris: http://xxx:9200
username: xxx
password: xxx
logging:
level:
# 输出es的查询参数(调试用)
tracer: TRACE

索引操作

实体类

@Data
@Document(indexName = "account")
public class Account { @Id
private String id;
// 解决ES中字段与实体类字段不一致的问题
@Field(name = "account_number", type = FieldType.Long)
private Long accountNumber;
@Field(type = FieldType.Text)
private String address;
@Field(type = FieldType.Integer)
private Integer age;
@Field(type = FieldType.Long)
private Long balance;
@Field(type = FieldType.Text)
private String city;
@Field(type = FieldType.Text)
private String email;
@Field(type = FieldType.Text)
private String employer;
@Field(type = FieldType.Text)
private String firstname;
@Field(type = FieldType.Text)
private String lastname;
@Field(type = FieldType.Text)
private String gender;
@Field(type = FieldType.Text)
private String state;
... 省略get、set 方法
}
IndexOperations idxOpt = template.indexOps(Account.class);
// 索引是否存在
boolean idxExist = idxOpt.exists(); // 创建索引
boolean createSuccess = idxOpt.createWithMapping();
System.out.println(createSuccess); // 删除索引
boolean deleted = idxOpt.delete();

文档操作(CRUD)

Account account = new Account();
account.setId("1");
account.setLastname("guyu"); // 这是插入或覆盖,如果id存在了就是覆盖
template.save(account); // 修改,用的是es的_update
template.update(account); // 删除
template.delete(account) // 批量新增(用的是es的_bulk)
List<Account> accountList = ...
template.save(accountList); // 根据id查询
Account account = template.get("1", Account.class);

搜索 + 排序 + 分页

// 搜索 firstname = Amber AND age = 32
Criteria criteria = new Criteria();
criteria.and(new Criteria("firstname").is("Amber"));
criteria.and(new Criteria("age").is(32)); // 分页
int pageNum = 1; // 页码
int pageSize = 20; // 每页数量
Query query = new CriteriaQueryBuilder(criteria)
.withSort(Sort.by(new Order(Sort.Direction.ASC, "age"))) // 排序字段1
.withSort(Sort.by(new Order(Sort.Direction.DESC, "balance"))) // 排序字段1
.withPageable(PageRequest.of(pageNum - 1, pageSize)) // 浅分页
// 不需要查询的字段
.withSourceFilter(new FetchSourceFilterBuilder().withExcludes("email", "address").build())
.build(); SearchHits<Account> searchHits = template.search(query, Account.class);
long totalValue = searchHits.getTotalHits(); // 匹配到的数量
for (SearchHit<Account> searchHit : searchHits.getSearchHits()) {
Account account = searchHit.getContent(); // 这就是得到的实体类
}

总结

本文介绍了 SpringBoot 整合 ElasticSearch 的两种方案,但均只是简单提及,更详细的用法需要自行查看官方文档。

ElasticSearch8 - SpringBoot整合ElasticSearch的更多相关文章

  1. SpringBoot整合ElasticSearch实现多版本的兼容

    前言 在上一篇学习SpringBoot中,整合了Mybatis.Druid和PageHelper并实现了多数据源的操作.本篇主要是介绍和使用目前最火的搜索引擎ElastiSearch,并和Spring ...

  2. ElasticSearch(2)---SpringBoot整合ElasticSearch

    SpringBoot整合ElasticSearch 一.基于spring-boot-starter-data-elasticsearch整合 开发环境:springboot版本:2.0.1,elast ...

  3. springboot整合elasticsearch入门例子

    springboot整合elasticsearch入门例子 https://blog.csdn.net/tianyaleixiaowu/article/details/72833940 Elastic ...

  4. Springboot整合elasticsearch以及接口开发

    Springboot整合elasticsearch以及接口开发 搭建elasticsearch集群 搭建过程略(我这里用的是elasticsearch5.5.2版本) 写入测试数据 新建索引book( ...

  5. SpringBoot整合Elasticsearch详细步骤以及代码示例(附源码)

    准备工作 环境准备 JAVA版本 java version "1.8.0_121" Java(TM) SE Runtime Environment (build 1.8.0_121 ...

  6. Springboot整合Elasticsearch报错availableProcessors is already set to [4], rejecting [4]

    Springboot整合Elasticsearch报错 今天使用SpringBoot整合Elasticsearch时候,相关的配置完成后,启动项目就报错了. nested exception is j ...

  7. Springboot整合ElasticSearch进行简单的测试及用Kibana进行查看

    一.前言 搜索引擎还是在电商项目.百度.还有技术博客中广泛应用,使用最多的还是ElasticSearch,Solr在大数据量下检索性能不如ElasticSearch.今天和大家一起搭建一下,小编是看完 ...

  8. 😊SpringBoot 整合 Elasticsearch (超详细).md

    SpringBoot 整合 Elasticsearch (超详细) 注意: 1.环境搭建 安装es Elasticsearch 6.4.3 下载链接 为了方便,环境使用Windows 配置 解压后配置 ...

  9. SpringBoot整合elasticsearch

    在这一篇文章开始之前,你需要先安装一个ElasticSearch,如果你是mac或者linux可以参考https://www.jianshu.com/p/e47b451375ea,如果是windows ...

  10. springboot整合elasticsearch(基于es7.2和官方high level client)

    前言 最近写的一个个人项目(传送门:全终端云书签)中需要用到全文检索功能,目前 mysql,es 都可以做全文检索,mysql 胜在配置方便很快就能搞定上线(参考这里),不考虑上手难度,es 在全文检 ...

随机推荐

  1. 利用nssm将jar包安装为windows服务

    1.介绍 假设我们有一个spring boot程序打包成jar文件放到windows服务器运行,第一种方式jar -jar xx方式在cmd运行.这样有个缺点,被别人误关闭了咋办?服务器重启了咋办? ...

  2. 编译原理LR分析

    LR(0)分析存在问题及解决办法 当LR(0)含有互相冲突的项目时,则需要向前展 望符号串,检查下一个输入符号的状态 例 项目集I={X→α· bβ,A→α·,B→α· } 当面临输入符号b时,应该选 ...

  3. 《系列二》-- 9、bean属性填充

    目录 一.概述: populateBean 在什么时候执行? 二.populateBean 的重要操作 三.重点操作一 propertyValue 的注入 3.1 根据 Bean名称注入 3.2 浅看 ...

  4. 如何保证消息顺序执行(Rabbitmq/kafka)

    转载: https://www.cnblogs.com/-wenli/p/13047059.html https://www.jianshu.com/p/02fdcb9e8784

  5. sql判断字符串中含中文方法

    基于UTF-8字符集 它是一种多字节字符集,编码为变长编码.那么它的编码范围根据:http://www.iteye.com/topic/977671 作者提供的资料学习,整理出它编码范围如下: u2e ...

  6. 【LeetCode回溯算法#02】组合总和III

    组合总和III 力扣题目链接(opens new window) 找出所有相加之和为 n 的 k 个数的组合.组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字. 说明: 所有数字 ...

  7. 扣子(coze.cn)| 由浅入深,手把手带你实现Java转型学习助手

    扣子(coze.cn)是一款用来开发新一代 AI Chat Bot 的应用编辑平台,无论你是否有编程基础,都可以通过这个平台来快速创建各种类型的 Chat Bot,并将其发布到各类社交平台和通讯软件上 ...

  8. jvm调优监控工具jps、jstack、jmap、jhat、jstat使用详解

    目录 前言 jps(Java Virtual Machine Process Status Tool) jstack jmap(Memory Map)和jhat(Java Heap Analysis ...

  9. 【Azure Redis 缓存】VM 里的 Redis 能直接迁移到 Azure Cache for Redis ? 需要改动代码吗?

    问题描述 原来部署在VM 里的 Redis 能直接迁移到 Azure Cache for Redis? 需要改动代码吗? 问题解答 以上问题需要从两个方面来解答. 第一:VM中Redis的数据转移到 ...

  10. 用 nebula_dart_gdbc 在移动设备玩图数据库,泰酷辣!

    nebula_dart_gdbc,是访问 NebulaGraph 的 Dart 语言客户端,在 dart_gdbc 的规范下进行开发. dart_gdbc 是一套使用 Dart 语言定义的图数据库标准 ...