ElasticSearch之SpringDataElasticsearch操作

tech2025-04-04  15

文章目录

1. Spring Data ElasticSearch简介1.1 什么是Spring Data1.2 什么是SpringDataES 2. 准备工作2.1 启动elasticsearch服务2.2 创建工程 3. 索引库的操作3.1 创建实体类Goods3.2 创建删除索引库以及类型映射 4. 使用ElasticsearchRepository对数据进行增删改4.1 新建 GoodRepository 接口4.2 创建单个文档4.3 批量创建文档4.4 修改和删除文档 5 查询文档数据5.1. 根据id查询5.2. 查询所有5.3. 使用search查询5.4. 使用search查询并分页排序 6. 使用ElasticsearchTemplate查询6.1. 分页和排序6.2. 高亮显示

1. Spring Data ElasticSearch简介

1.1 什么是Spring Data

spring-boot-starter-data-redis > redisTemplate

Spring Data 是一个用于简化数据访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷。 Spring Data可以极大的简化数据操作的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一些常用的功能。

查看 Spring Data的官网:https://spring.io/projects/spring-data

1.2 什么是SpringDataES

SpringDataElasticsearch(以后简称SDE)是Spring Data项目下的一个子模块,是Spring提供的操作ElasticSearch的数据层,封装了大量的基础操作,通过它可以很方便的操作ElasticSearch的数据。 Spring Data 的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如MySQL),还是非关系数据库(如Redis),或者类似Elasticsearch这样的索引数据库。从而简化开发人员的代码,提 高开发效率。

包含很多不同数据操作的模块:

Spring Data Elasticsearch的页面:https://projects.spring.io/spring-data-elasticsearch/

特征:

支持Spring的基于@Configuration的java配置方式,或者XML配置方式提供了用于操作ES的便捷工具类ElasticsearchTemplate。包括实现文档到POJO之间的自动智能映射。利用Spring的数据转换服务实现的功能丰富的对象映射基于注解的元数据映射方式,而且可扩展以支持更多不同的数据格式根据持久层接口自动生成对应实现方法,无需人工编写基本操作代码(类似mybatis,根据接口自动得到实现)。当然,也支持人工定制查询

2. 准备工作

2.1 启动elasticsearch服务

2.2 创建工程

导入依赖 yml配置文件信息:

server: port: 8081 spring: application: name: spring-data-es data: elasticsearch: cluster-name: elasticsearch # 集群名称 cluster-nodes: 127.0.0.1:9300 # 节点信息 tcp端口号

需要注意的是,SpringDataElasticsearch底层使用的不是Elasticsearch提供的RestHighLevelClient,而是TransportClient,并不采用Http协议通信,而是访问elasticsearch对外开放的tcp端口,ElasticSearch默认tcp端口。 另外,SpringBoot已经帮我们配置好了各种SDE配置,并且注册了一个ElasticsearchTemplate供我们使用。

3. 索引库的操作

3.1 创建实体类Goods

@Data @AllArgsConstructor @NoArgsConstructor //集群时可以设置 : shards:分片数量(默认值:5),replicas:副本数量(默认值:1) @Document(indexName = "goods", type = "goods") public class Goods { /** * 必须有id,这里的id是全局唯一的标识,等同于es中的“_id” */ @Id private Long id; /** * 标题 * type: 字段数据类型 * analyzer: 分词器类型 * index: 是否索引(默认值:true) * store: 是否存储(默认值:false) */ @Field(type = FieldType.Text,analyzer = "ik_max_word") private String title; /** * 分类 */ @Field(type = FieldType.Keyword) private String category; /** * 品牌 */ @Field(type = FieldType.Keyword) private String brand; /** * 价格 */ @Field(type = FieldType.Double) private Double price; /** * 图片地址 */ @Field(type = FieldType.Keyword,index = false) private String images; }

几个用到的注解:

@Document:声明索引库配置 indexName:索引库名称type:类型名称,默认是“docs”shards:分片数量,默认5replicas:副本数量,默认1 @Id:声明实体类的id@Field:声明字段属性 type:字段的数据类型analyzer:指定分词器类型index:是否创建索引 默认为truestore:是否存储 默认为false

3.2 创建删除索引库以及类型映射

进入test文件夹下面进行单元测试

@RunWith(SpringRunner.class) @SpringBootTest public class SpringEs01ApplicationTests { @Autowired private ElasticsearchTemplate elasticsearchTemplate; //创建索引库 @Test public void testCreateIndex(){ boolean index = elasticsearchTemplate.createIndex(Goods.class); System.out.println("goods索引"+(index?"":"没有")+"创建成功"); } //创建类型和映射 @Test public void testPutMapping(){ boolean flag = elasticsearchTemplate.putMapping(Goods.class); System.out.println("goods映射"+(flag?"":"没有")+"创建成功"); } //删除索引库 @Test public void testDeleteIndex(){ boolean flag = elasticsearchTemplate.deleteIndex(Goods.class); System.out.println("删除索引库"+(flag?"成功":"失败")); } }

4. 使用ElasticsearchRepository对数据进行增删改

4.1 新建 GoodRepository 接口

SDE的文档索引数据CRUD并没有封装在ElasticsearchTemplate中,而是有一个叫做ElasticsearchRepository的接口:

public interface GoodsEsRepository extends ElasticsearchRepository<Goods,Long> { }

执行springboot的main方法, 发现创建了 索引库和映射type

4.2 创建单个文档

@RunWith(SpringRunner.class) @SpringBootTest public class GoodsESRepositoryTest { @Autowired private GoodsESRepository goodsESRepository; @Test public void demo01() { Goods goods = new Goods(1L, "华为手机1", "手机", "华为", 1999.0, "http://wwww.baidu.com"); goodsESRepository.save(goods); } }

4.3 批量创建文档

@Test public void demo02() { // 需求: 批量新建 List<Goods> goodsList = new ArrayList<Goods>(); for (long i = 2; i < 10 ; i++) { Goods goods = new Goods(i, "小米手机" + i, "手机", "小米", 1999.0 + 1000, "http://wwww.baidu.com"); goodsList.add(goods); } goodsESRepository.saveAll(goodsList); }

4.4 修改和删除文档

@Test public void demo05() { // 需求5: 修改 // 1 先查 Optional<Goods> optional = goodsRepository.findById(2L); if(optional.isPresent()) { Goods goods = optional.get(); System.out.println("=========== " + goods); // 2 再改 goods.setTitle(goods.getTitle() + "22222222222"); goods.setPrice(300D); goodsRepository.save(goods); } } @Test public void demo06() { // 需求5: 删除 goodsRepository.deleteById(9L); }

5 查询文档数据

默认提供了根据id查询, 查询所有两个功能

5.1. 根据id查询

调用goods仓库根据id查询判断返回 Optional 对象是有有值从Optional 对象中获取查询结果 @Test public void demo03() { // 需求: 根据id查询商品信息 Optional<Goods> optional = goodsESRepository.findById(6); if(optional.isPresent()) { Goods goods = optional.get(); System.out.println(goods); } }

5.2. 查询所有

@Test public void demo04() { // 需求: 查询所有 Iterable<Goods> it = goodsESRepository.findAll(); for (Goods goods : it) { System.out.println(goods); } }

5.3. 使用search查询

构建QueryBuilder 对象设置查询类型和查询条件调用good仓库search方法进行查询遍历打印输出查询结果 @Test public void demo05() { // 需求: 查询title中含有华为的数据 QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "华为"); Iterable<Goods> it = goodsESRepository.search(queryBuilder); for (Goods goods : it) { System.out.println(goods); } }

5.4. 使用search查询并分页排序

构建Sort排序对象,指定排序字段和排序方式使用PageRequest构建Pageable分页对象,指定分页参数,并将排序对象设置到分页对象中调用goods仓库search方法进行查询解析结果 @Test public void demo06() { // 需求: 查询所有 且 按照id排序 // 1. 构建Sort排序对象,指定排序字段和排序方式 Sort sort = new Sort(Sort.Direction.ASC, "id"); // 2. 使用PageRequest构建Pageable分页对象,指定分页参数,并将排序对象设置到分页对象中 Pageable pageable = PageRequest.of(0, 3, sort); // 3. 调用goods仓库search方法进行查询 Page<Goods> page = goodsESRepository.search(QueryBuilders.matchAllQuery(), pageable); // 4 解析结果 // 4.1 获取总记录数 long totalElements = page.getTotalElements(); System.out.println("总记录数: ===== " + totalElements); // 4.2 获取总页数 int totalPages = page.getTotalPages(); System.out.println("总页数: ===== " + totalPages); // 4.2 获取当前页的数据 List<Goods> goodsList = page.getContent(); for (Goods goods : goodsList) { System.out.println("========== " + goods); } }

6. 使用ElasticsearchTemplate查询

SDE也支持使用ElasticsearchTemplate进行原生查询

而查询条件的构建是通过一个名为NativeSearchQueryBuilder的类来完成的,不过这个类的底层还是使用的原生API中的QueryBuilders、HighlightBuilders等工具。

6.1. 分页和排序

可以通过NativeSearchQueryBuilder类来构建分页和排序、聚合等操作

queryBuilder.withQuery() //设置查询类型和查询条件

queryBuilder.withPageable() //设置分页

queryBuilder.withSort()//设置排序

构建NativeSearchQueryBuilder查询对象使用QueryBuilders指定查询类型和查询条件使用SortBuilders指定排序字段和排序方式使用PageRequest对象指定分页参数调用NativeSearchQueryBuilder的build方法完成构建使用ElasticsearchTemplate完成查询解析结果 @RunWith(SpringRunner.class) @SpringBootTest public class GoodsESRepositoryTest { @Autowired private ElasticsearchTemplate elasticsearchTemplate; /** * 使用ElasticsearchTemplate完成分页排序查询 */ @Test public void nativeSearchQuery(){ //1.构建NativeSearchQueryBuilder查询对象 NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder(); //2.使用QueryBuilders指定查询类型和查询条件 searchQueryBuilder.withQuery(QueryBuilders.matchQuery("title","小米")); //3.使用SortBuilders指定排序字段和排序方式 searchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC)); //4.使用PageRequest对象指定分页参数 searchQueryBuilder.withPageable(PageRequest.of(0,2)); //5.调用NativeSearchQueryBuilder的build方法完成构建 NativeSearchQuery searchQuery = searchQueryBuilder.build(); //6.使用ElasticsearchTemplate完成查询 AggregatedPage<Goods> page = elasticsearchTemplate.queryForPage(searchQuery, Goods.class); //7.解析结果 //7.1获取总记录数 long totalElements = page.getTotalElements(); System.out.println("totalElements: " + totalElements); //7.2获取页总数 int totalPages = page.getTotalPages(); System.out.println("totalPages: " + totalPages); //7.3遍历查询结果 for (Goods goods : page) { System.out.println("结果 : " + goods); } } }

6.2. 高亮显示

@Test public void demo05() { // 增加高亮显示 //1. 构建NativeSearchQueryBuilder查询对象 NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); // 8 高亮显示的效果 nativeSearchQueryBuilder.withHighlightFields(new HighlightBuilder.Field("title").preTags("<span style='color:blue;'>").postTags("</span>")); //2. 使用QueryBuilders指定查询类型和查询条件 nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("title", "华为")); //3. 使用SortBuilders指定排序字段和排序方式 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.ASC)); //4. 使用PageRequest对象指定分页参数 nativeSearchQueryBuilder.withPageable(PageRequest.of(1,3)); //5. 调用NativeSearchQueryBuilder的build方法完成构建 NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build(); //6. 使用ElasticsearchTemplate完成查询 // 为了高亮显示 改造第6步 AggregatedPage<Goods> page = elasticsearchTemplate.queryForPage(searchQuery, Goods.class, new SearchResultMapper() { @Override public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) { // 封装数据的对象 List<Goods> goodsList = new ArrayList<Goods>(); // 1 获取命中对象 SearchHits hits = response.getHits(); // 2 判断 校验是否为空, // 2.1 如果为空, 直接返回null; 否则 继续执行 if(hits.getHits().length<=0) { return null; } // 2.2 遍历 for (SearchHit hit : hits) { // 目标: 将查询结果封装goods对象中 再放到集合中 // 1 创建goods对象 Goods goods = new Goods(); // 2 设置内容 // 设置id String id = hit.getId(); goods.setId(Long.parseLong(id)); // 设置分类属性 String category = hit.getSourceAsMap().get("category").toString(); if(category != null) { goods.setCategory(category); } // 设置 brand 品牌属性 String brand = hit.getSourceAsMap().get("brand").toString(); if(brand != null) { goods.setBrand(brand); } // 设置 brand 品牌属性 String images = hit.getSourceAsMap().get("images").toString(); if(images != null) { goods.setImages(images); } // 设置价格 String price = hit.getSourceAsMap().get("price").toString(); if(price != null) { goods.setPrice(Double.parseDouble(price)); } // 设置高亮字段 if(hit.getHighlightFields().size()>0) { String title = hit.getHighlightFields().get("title").fragments()[0].toString(); goods.setTitle(title); } // 3 将goods对象放到 集合中 goodsList.add(goods); } return new AggregatedPageImpl<>((List<T>) goodsList); } }); //7. 解析结果 // 7.1 获取总记录数 System.out.println("总记录数 ====" + page.getTotalElements()); // 7.2 获取总页数 System.out.println("总页数 ====" + page.getTotalPages()); // 7.3 获取每条记录信息 // List<Goods> goodsList = page.getContent(); for (Goods goods : page) { System.out.println("====================="); System.out.println(goods); } }
最新回复(0)