文章目录
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
需要注意的是,SpringDataElasticsearch底层使用的不是Elasticsearch提供的RestHighLevelClient,而是TransportClient,并不采用Http协议通信,而是访问elasticsearch对外开放的tcp端口,ElasticSearch默认tcp端口。 另外,SpringBoot已经帮我们配置好了各种SDE配置,并且注册了一个ElasticsearchTemplate供我们使用。
3. 索引库的操作
3.1 创建实体类Goods
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName
= "goods", type
= "goods")
public class Goods {
@Id
private Long id
;
@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() {
Optional
<Goods> optional
= goodsRepository
.findById(2L
);
if(optional
.isPresent()) {
Goods goods
= optional
.get();
System
.out
.println("=========== " + goods
);
goods
.setTitle(goods
.getTitle() + "22222222222");
goods
.setPrice(300D);
goodsRepository
.save(goods
);
}
}
@Test
public void demo06() {
goodsRepository
.deleteById(9L
);
}
5 查询文档数据
默认提供了根据id查询, 查询所有两个功能
5.1. 根据id查询
调用goods仓库根据id查询判断返回 Optional 对象是有有值从Optional 对象中获取查询结果
@Test
public void demo03() {
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() {
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() {
Sort sort
= new Sort(Sort
.Direction
.ASC
, "id");
Pageable pageable
= PageRequest
.of(0, 3, sort
);
Page
<Goods> page
= goodsESRepository
.search(QueryBuilders
.matchAllQuery(), pageable
);
long totalElements
= page
.getTotalElements();
System
.out
.println("总记录数: ===== " + totalElements
);
int totalPages
= page
.getTotalPages();
System
.out
.println("总页数: ===== " + totalPages
);
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
;
@Test
public void nativeSearchQuery(){
NativeSearchQueryBuilder searchQueryBuilder
= new NativeSearchQueryBuilder();
searchQueryBuilder
.withQuery(QueryBuilders
.matchQuery("title","小米"));
searchQueryBuilder
.withSort(SortBuilders
.fieldSort("id").order(SortOrder
.DESC
));
searchQueryBuilder
.withPageable(PageRequest
.of(0,2));
NativeSearchQuery searchQuery
= searchQueryBuilder
.build();
AggregatedPage
<Goods> page
= elasticsearchTemplate
.queryForPage(searchQuery
, Goods
.class);
long totalElements
= page
.getTotalElements();
System
.out
.println("totalElements: " + totalElements
);
int totalPages
= page
.getTotalPages();
System
.out
.println("totalPages: " + totalPages
);
for (Goods goods
: page
) {
System
.out
.println("结果 : " + goods
);
}
}
}
6.2. 高亮显示
@Test
public void demo05() {
NativeSearchQueryBuilder nativeSearchQueryBuilder
= new NativeSearchQueryBuilder();
nativeSearchQueryBuilder
.withHighlightFields(new HighlightBuilder.Field("title").preTags("<span style='color:blue;'>").postTags("</span>"));
nativeSearchQueryBuilder
.withQuery(QueryBuilders
.matchQuery("title", "华为"));
nativeSearchQueryBuilder
.withSort(SortBuilders
.fieldSort("id").order(SortOrder
.ASC
));
nativeSearchQueryBuilder
.withPageable(PageRequest
.of(1,3));
NativeSearchQuery searchQuery
= nativeSearchQueryBuilder
.build();
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>();
SearchHits hits
= response
.getHits();
if(hits
.getHits().length
<=0) {
return null
;
}
for (SearchHit hit
: hits
) {
Goods goods
= new Goods();
String id
= hit
.getId();
goods
.setId(Long
.parseLong(id
));
String category
= hit
.getSourceAsMap().get("category").toString();
if(category
!= null
) {
goods
.setCategory(category
);
}
String brand
= hit
.getSourceAsMap().get("brand").toString();
if(brand
!= null
) {
goods
.setBrand(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
);
}
goodsList
.add(goods
);
}
return new AggregatedPageImpl<>((List
<T>) goodsList
);
}
});
System
.out
.println("总记录数 ====" + page
.getTotalElements());
System
.out
.println("总页数 ====" + page
.getTotalPages());
for (Goods goods
: page
) {
System
.out
.println("=====================");
System
.out
.println(goods
);
}
}