前言

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. NC14352 旅行

    题目链接 题目 题目描述 小z放假了,准备到R城市旅行,其中这个城市有N个旅游景点.小z时间有限,只能在三个旅行景点进行游玩.小明租了辆车,司机很善良,说咱不计路程,只要你一次性缴费足够,我就带你走遍 ...

  2. NC214362 第k小

    题目链接 题目 题目描述 有一个长度为n的数组,值为 a[i], 牛牛想找到数组中第 k 小的数.比如 1 2 2 3 4 6 中,第 3 小的数就是2. 牛牛觉得这个游戏太简单了,想加一点难度,现在 ...

  3. test-01-java 单元测试框架 junit 入门介绍

    拓展阅读 junit5 系列 基于 junit5 实现 junitperf 源码分析 Auto generate mock data for java test.(便于 Java 测试自动生成对象信息 ...

  4. 【Unity3D】摇杆

    1 需求实现 ​ 1)需求实现 鼠标拖拽摇杆球控制坦克移动: 上下左右按键也可以控制坦克移动,并且摇杆球也同步移动: 鼠标右键滑动,控制坦克转向: 相机在玩家后上方的位置,始终跟随玩家,朝玩家正前方看 ...

  5. Java集合框架学习(十三) Collections类详解

    Collections类介绍 这个类操作或返回集合的专有静态方法. 它包含多态算法,利用wrappers(封装器)返回指定集合类型的新集合,以及其他一些零散功能. 如果该类的方法引用的集合或类对象为n ...

  6. 《系列一》-- 4、xml配置文件解析之[默认]命名空间[标签]的解析

    阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证.全系列文章基于 spring 源码 5.x 版本. Spring源码阅读系列--全局目录.md ...

  7. 【Android逆向】破解看雪9月算法破解第三题

    这题的目标是算法还原,并写出注册机 1. 9月份算法第一题.apk 安装到手机 2. 随意输入账号密码,提示错误 3. apk拖入到jadx中 public native boolean regist ...

  8. 教你如何判断Java代码中异步操作是否完成

    本文分享自华为云社区<java代码实现异步返回结果如何判断异步执行完成>,作者: 皮牙子抓饭. 在许多应用程序中,我们经常使用异步操作来提高性能和响应度.在Java中,我们可以使用多线程或 ...

  9. 正则函数及面向对象开发初识---day19

    1.正则函数 # ### 正则函数 import re #search 通过正则匹配出第一个对象返回,通过group取出对象中的值 strvar = "1+2 3*4" obj = ...

  10. Kotlin 协程二 —— 通道 Channel

    目录 一. Channel 基本使用 1.1 Channel 的概念 1.2 Channel 的简单使用 1.3 Channel 的迭代 1.4 close 关闭 Channel 1.5 Channe ...