写总结前的废话O(∩_∩)O
ok啊,现在也是开始学习把ES融合到SpringBoot框架中去。我的jdk版本是17,然后idea是24年的新版本。然后大家能学习这个的我觉得都是把java基础全学完了的,而且进度也应该是学到了黑马商城的微服务框架的。这个基础的Java环境配置就不多说了,直接开干!<( ̄︶ ̄)↗[GO!]
然后就正常的去准备一个SpringBoot框架的后端项目就行了,这个拉取仓库的地址我是换成了阿里云的了,最好还是不要换吧。其实官网的构建速度其实也还行。
OK啊现在是2025年6月6日,00点38分了。我从10.00多吧开始学,学到现在刚好结束了。主要是晚上真的没有心思去学这个了,室友和自我欲望的无限扩大,今天很失败,本来一个上午就能弄完的事情,哎不多说了,睡觉明天继续。明天应该就可以结束这块的内容学习了,然后后面应该就不会再更新了,要开始去准备期末复习了。到时候再说吧,万一我又发现了新的好玩的内容,我也会开始学习并更新日志记录我的学习路程,也给大家达到教学的效果,这样如果你们都知道了,那我的知识其实也都掌握了!拜拜
安装一个web我觉得应该是够用了,记得要是3.X的springboot框架哈,他这个不稳定有时候默认就换成了2.X的版本。大家注意一下就行了。
一 ES客户端介绍和SpringBoot怎么整合
- ElasticSearch是搜索引擎,作为服务端程序,提供了HTTP的Restful接口接入
- 因此多个不同的语言都可以轻松接入ES搜索功能
Java API Client(8.X版本开始推荐使用)
- Elasticsearch在7.1版本之前使用的Java客户端是Java REST Client
- 从7.1版本开始Elastic官方将Java REST Client标记为弃用(deprecated),推荐使用新版Java客户端Java API Client
- 新版的java API Client是一个用于与Elasticsearch服务器进行通信的Java客户端库
- 封装了底层的Transport通信,并提供了同步和异步调用、流式和函数式调用等方法
- 官网文档地址
- https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/8.5/introduction.html
其他的一些我就不去了解了,可能之后找实习会去看看吧,现在还是已快速了解使用为主
SpringBoot有两种方案去结合ES使用
方案一:使用Elasticsearch 官方提供的高级客户端库 - Elasticsearch Api Client
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.5.3</version>
</dependency>
方案二:使用 Spring Data Elasticsearch
<!--这个starter里面就是依赖 spring-data-elasticsearch-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
- 基于 Spring Data 的标准化数据访问技术,简化了与 Elasticsearch 的集成。
- 提供了丰富的 CRUD 操作和查询方法,简化了数据访问,包括自动化的索引管理和映射
- Spring Data Elasticsearch 对于一些高级功能和复杂查询可能不够灵活,需要额外定制处理
ok啊到这里我也是去Spring官网看了下,这个Spring Data也是一种框架,他这个其实也还可以,Spring Data框架的简介
- 是一个用于简化数据访问和持久化的开发框架,提供了一组统一的 API 和抽象
- 与各种数据存储技术(如关系型数据库、NoSQL 数据库、Elasticsearch 等)进行交互变得更加容易
- 官网:https://spring.io/projects/spring-data
里面有四个核心的模块,我来把官网的介绍CV一下把,大家看看其实说的也还可以至少我理解是没有问题的
- Spring Data JPA
- 用于与关系型数据库进行交互,基于 JPA(Java Persistence API)标准
- 提类似于 Repository 的接口,通过继承这些接口并声明方法,自动生成常见的数据CRUD
- Spring Data MongoDB
- 用于与 MongoDB NoSQL 数据库进行交互,提供一种类似于 Repository 的接口
- 通过继承这些接口并声明方法,自动生成常见的数据CRUD
- Spring Data Redis
- 用于与 Redis 键值存储数据库进行交互,提供 Repository 的接口
- 通过继承这些接口并声明方法,自动生成常见的数据CRUD
- Spring Data Elasticsearch
- 用于与 Elasticsearch 搜索引擎进行交互,提供 Repository 的接口
- 通过继承这些接口并声明方法,自动生成常见的数据CRUD
欧克啊,最后也是选择方案二来进行学习使用了,先添加依赖,添加完之后就开始在配置文件写入对应的配置就行了
spring:
elasticsearch:
uris: http://192.168.150.103:9200
大家这块就输入自己的服务器地址就行了,或者像我这样输入自己的虚拟机网络地址ip也可以。前期其实到这就结束了后面就是学习该怎么在后端那边对ES进行CRUD了。
二 SpringBoot操作ES
1) ElasticsearchTemplate索引库操作
什么是ElasticsearchTemplate
- 是 Spring Data Elasticsearch 提供的一个核心类,是 ElasticsearchClient 的一个具体实现
- 用于在 Spring Boot 中操作 Elasticsearch 进行数据的存取和查询
- 提供了一组方法来执行各种操作,如保存、更新、删除和查询文档,执行聚合操作等
ElasticsearchTemplate 的一些常用方法
save(Object)
: 保存一个对象到 Elasticsearch 中。index(IndexQuery)
: 使用 IndexQuery 对象执行索引操作。delete(String, String)
: 删除指定索引和类型的文档。get(String, String)
: 获取指定索引和类型的文档。update(UpdateQuery)
: 使用 UpdateQuery 对象执行更新操作。search(SearchQuery, Class)
: 执行搜索查询,并将结果映射为指定类型的对象。count(SearchQuery, Class)
: 执行搜索查询,并返回结果的计数
OK啊我觉得这块说实话大家过过眼就行,到时候用的多的话其实问题应该也不大
ElasticsearchTemplate 常见注解配置(都是属于spring data elasticsearch)
- @Id 指定主键
- @Document指定实体类和索引对应关系
indexName:索引名称
- @Field指定普通属性
type 对应Elasticsearch中属性类型,使用FiledType枚举快速获取。
text 类型能被分词
keywords 不能被分词
index 是否创建索引,作为搜索条件时index必须为true
analyzer 指定分词器类型。
!上案例<( ̄︶ ̄)↗[GO!]!
对了在这之前大家再引入下Lombok依赖吧,后续直接用了。
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
创建DTO(大家导包不要导错了哈,都是springframework.data包下的)
@Data
@Document(indexName = "video")
public class VideoDTO {
@Id
@Field(type = FieldType.Text, index = false)
private Long id;
@Field(type = FieldType.Text)
private String title;
@Field(type = FieldType.Text)
private String description;
@Field(type = FieldType.Keyword)
private String category;
@Field(type = FieldType.Integer)
private Integer duration;
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
private LocalDateTime createTime;
}
安排测试用例,大家可以看看其实这块真不难
@SpringBootTest
class LearnEsApplicationTests {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
/**
* 判断索引是否存在
*/
@Test
void existsIndex(){
IndexOperations indexOperations = elasticsearchTemplate.indexOps(VideoDTO.class);
boolean exists = indexOperations.exists();
System.out.println(exists);
}
/**
* 创建索引
*/
@Test
void createIndex(){
// spring data es 所有索引操作都在这个案例测试中
IndexOperations indexOperations = elasticsearchTemplate.indexOps(VideoDTO.class);
// 如果索引存在就删除
if(indexOperations.exists()){
indexOperations.delete();
}
// 创建索引
indexOperations.create();
// 设置映射:在正式开发中几乎不会使用框架创建索引或设置映射,这是管理员和框架师做的的事情,不适合用代码实现
elasticsearchTemplate.indexOps(VideoDTO.class).putMapping();
}
/**
* 删除索引
*/
@Test
void deleteIndex(){
IndexOperations indexOperations = elasticsearchTemplate.indexOps(VideoDTO.class);
boolean delete = indexOperations.delete();
System.out.println(delete);
}
}
2)操作ES文档Documen
**!上案例<( ̄︶ ̄)↗[GO!]!**不搞虚的了直接介绍SpringBoot中的操作代码
/**
* 新增文档
*/
@Test
void insertDocument() {
VideoDTO videoDTO = new VideoDTO();
videoDTO.setId(1L);
videoDTO.setTitle("课堂架构大课和Spring Cloud");
videoDTO.setCreateTime(LocalDateTime.now());
videoDTO.setDuration(100);
videoDTO.setCategory("后端");
videoDTO.setDescription("这个是综合大型课程,包括了jvm,redis,新版spring boot3.x,架构,监控,性能优化,算法,高并发等多方面内容");
VideoDTO saved = elasticsearchTemplate.save(videoDTO);
System.out.println(saved);
}
/**
* 修改文档
*/
@Test
void updateDocument() {
VideoDTO videoDTO = new VideoDTO();
videoDTO.setId(1L);
videoDTO.setTitle("小滴课堂架构大课和Spring Cloud V2");
videoDTO.setCreateTime(LocalDateTime.now());
videoDTO.setDuration(102);
videoDTO.setCategory("后端");
videoDTO.setDescription("这个是综合大型课程,包括了jvm,redis,新版spring boot3.x,架构,监控,性能优化,算法,高并发等多方面内容");
VideoDTO saved = elasticsearchTemplate.save(videoDTO);
System.out.println(saved);
}
/**
* 批量新增文档
*/
@Test
void batchInsertDocument() {
List<VideoDTO> list = new ArrayList<>();
list.add(new VideoDTO(2L, "录制的按摩课程", "主要按摩和会所推荐", 123, "后端", LocalDateTime.now()));
list.add(new VideoDTO(3L, "前端性能优化", "前端高手系列", 100042, "前端", LocalDateTime.now()));
list.add(new VideoDTO(4L, "海量数据项目大课", "D哥的后端+大数据综合课程", 5432345, "后端", LocalDateTime.now()));
list.add(new VideoDTO(5L, "课堂永久会员", "可以看海量专题课程,IT技术持续充电平台", 6542, "后端", LocalDateTime.now()));
list.add(new VideoDTO(6L, "前端低代码平台", "高效开发底层基础平台,效能平台案例", 53422, "前端", LocalDateTime.now()));
list.add(new VideoDTO(7L, "自动化测试平台大课", "微服务架构下的spring cloud架构大课,包括jvm,效能平台", 6542, "后端", LocalDateTime.now()));
Iterable<VideoDTO> result = elasticsearchTemplate.save(list);
System.out.println(result);
}
/**
* 根据id查询文档
*/
@Test
void searchDocumentById(){
VideoDTO videoDTO = elasticsearchTemplate.get("3", VideoDTO.class);
assert videoDTO != null;
System.out.println(videoDTO);
}
/**
* 根据id删除文档
*/
@Test
void deleteDocumentById() {
String delete = elasticsearchTemplate.delete("2", VideoDTO.class);
System.out.println(delete);
}
这块其实很简单吧,就是调用es的一个方法去对文档的一些列增删改查。还简单的,比起之前的那种。。。
3) ES搜索操作
欧克到了这个搜索的正经的一个我的需求操作要开始认真去弄明白了至少要知道90%了。对于新版本的8.X版本其实差别还是很大的
新版的ElasticSearch的Query接口
Query是Spring Data Elasticsearch的接口,有多种具体实现。
CriteriaQuery
- 创建Criteria来搜索数据,而无需了解 Elasticsearch 查询的语法或基础知识
- 允许用户通过简单地连接和组合,指定搜索文档必须满足的对象来构建查询
StringQuery
- 将Elasticsearch查询作为JSON字符串,更适合对Elasticsearch查询的语法比较了解的人
- 也更方便使用kibana或postman等客户端工具行进调试
NativeQuery
- 复杂查询或无法使用CriteriaAPI 表达的查询时使用的类,例如在构建查询和使用聚合的场景
!上案例<( ̄︶ ̄)↗[GO!]!
ES8.X的版本较于7.X版本,查询采用新版的lambda表达式语法,更简洁。这块其实真的很容易,其实就是构建搜索条件,然后调用ES的搜索方法。就行了。so easy~
/**
* 查询所有文档
*/
@Test
void selectAll(){
SearchHits<VideoDTO> search = elasticsearchTemplate.search(Query.findAll(), VideoDTO.class);
List<SearchHit<VideoDTO>> searchHits = search.getSearchHits();
List<VideoDTO> videoDTOS = new ArrayList<>();
// 获得searchHits,遍历得到content
searchHits.forEach( hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
/**
* match查询
*/
@Test
void matchQuery(){
// 其实这个可以和mybatisPlus差不多构建搜索条件
Query query = NativeQuery.builder()
.withQuery(q ->
q.match(m -> m
.field("title") // 匹配字段
.query("框架"))) // 匹配内容
.build();
SearchHits<VideoDTO> searchHits = elasticsearchTemplate.search(query, VideoDTO.class);
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach( hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
/**
* 分页查询
*/
@Test
void pageSearch() {
Pageable pageable = Pageable.ofSize(3).withPage(0);
// 构建查询条件
Query query = NativeQuery.builder()
.withQuery(q -> q.matchAll(b -> b))
.withPageable(pageable)
.build();
SearchHits<VideoDTO> searchHits = elasticsearchTemplate.search(query, VideoDTO.class);
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
/**
* 排序查询,根据时长降序排列
*/
@Test
void sortSearch() {
Query query = NativeQuery.builder().withQuery(a -> a.matchAll(b -> b))
.withPageable(Pageable.ofSize(10).withPage(0))
.withSort(Sort.by("duration").descending()).build();
SearchHits<VideoDTO> searchHits = elasticsearchTemplate.search(query, VideoDTO.class);
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
4) ES原始StringQuery搜索
什么是StringQuery
- 将Elasticsearch查询作为JSON字符串,更适合对Elasticsearch查询的语法比较了解的人
- 也更方便使用kibana或postman等客户端工具行进调试
!上案例<( ̄︶ ̄)↗[GO!]!
原始的DSL查询语句
GET /video/_search
{
"query" : {
"bool" : {
"must" : [
{ "match" : {"title" : "架构"}},
{ "match" : {"description" : "spring"}},
{ "rang" : { "duration" : {
"gte" : 10,
"lte" : 6000}}}
]
}
}
}
SpringBoot+SpringData查询
//搜索标题有 架构 关键词,描述有 spring关键字,时长范围是 10~6000之间的
@Test
void stringQuery() {
String dsl = """
{"bool":{"must":[{"match":{"title":"架构"}},{"match":{"description":"spring"}},{"range":{"duration":{"gte":10,"lte":6000}}}]}}
""";
Query query = new StringQuery(dsl);
List<SearchHit<VideoDTO>> searchHitList = elasticsearchTemplate.search(query, VideoDTO.class).getSearchHits();
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHitList.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
5) ES聚合搜索
这里的话其实学完4)部分的话我们就可以采用两种形式去写这个query条件的语句了
- 方案一:可以使用原始DSL进行处理【在SpringBoot3.X版本其实已经不推荐用这个东西了】
- 方案二:使用NativeQuery完成聚合搜索
!上案例<( ̄︶ ̄)↗[GO!]!
- 统计不同分类下的视频数量
/**
* 聚合查询
*/
@Test
void aggQuery() {
Query query = NativeQuery.builder()
.withAggregation("category_group", Aggregation.of(a -> a
.terms(ta -> ta.field("category").size(2))))
.build();
SearchHits<VideoDTO> searchHits = restTemplate.search(query, VideoDTO.class);
//获取聚合数据
ElasticsearchAggregations aggregationsContainer = (ElasticsearchAggregations) searchHits.getAggregations();
Map<String, ElasticsearchAggregation> aggregations = Objects.requireNonNull(aggregationsContainer).aggregationsAsMap();
//获取对应名称的聚合
ElasticsearchAggregation aggregation = aggregations.get("category_group");
Buckets<StringTermsBucket> buckets = aggregation.aggregation().getAggregate().sterms().buckets();
//打印聚合信息
buckets.array().forEach(bucket -> {
System.out.println("组名:"+bucket.key().stringValue() + ", 值" + bucket.docCount());
});
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
group");
Buckets buckets = aggregation.aggregation().getAggregate().sterms().buckets();
//打印聚合信息
buckets.array().forEach(bucket -> {
System.out.println("组名:"+bucket.key().stringValue() + ", 值" + bucket.docCount());
});
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}