Spring Data 整合 ElasticSearch
Spring Data ElasticSearch简介
1 什么是Spring Data
Spring Data是一个用于简化持久层数据访问的开源框架。其主要目标是使得对数据的访问变得方便快捷。 Spring Data可以极大的简化数据操作的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。包括CRUD外,还包括如分页、排序等一些常用的功能,几乎可以节省持久层代码80%以上的编码工作量。
2 什么是Spring Data ElasticSearch
Spring Data ElasticSearch 基于 spring data API 简化 elasticSearch操作,将原始操作elasticSearch的客户端API 进行封装 。Spring Data为Elasticsearch项目提供集成搜索引擎。Spring Data Elasticsearch POJO的关键功能区域为中心的模型与Elastichsearch交互文档和轻松地编写一个存储库数据访问层。
用来操作ElasticSearch的框架,使得开发更加便捷
环境搭建
实现步骤:
- 创建SpringBoot的项目
- 勾选starter依赖坐标
- 编写持久层接口GoodDao,编写pojo实体类
- 配置文件,集群配置,ElasticSearch服务地址http://127.0.0.1:9300
实现过程:
-
创建SpringBoot的项目
-
勾选starter依赖坐标
-
编写持久层接口GoodDao,编写pojo实体类
public interface GoodDao { }
pojo实体类,商品good
/**
* indexName 设置索引库名称
* type 设置类型的名称
* shards 设置分片数,默认是5
* replicas 设置副本数,默认是1
*/
@Data
@Builder
@Document(indexName = "springdataelastic",type = "goods",shards = 5,replicas = 1)
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;//图片地址
//getter,setter,toString
}
- 配置文件,集群配置,ElasticSearch服务地址http://127.0.0.1:9300
#配置集群的名称
spring.data.elasticsearch.cluster-name=elasticsearch
#配置集群的服务地址
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
常用操作
1、创建索引库操作
几个用到的注解:
- @Document:声明索引库配置
- indexName:索引库名称
- type:类型名称,默认是“docs”
- shards:分片数量,默认5
- replicas:副本数量,默认1
- @Id:声明实体类的id
- @Field:声明字段属性
- type:字段的数据类型
- analyzer:指定分词器类型
- index:是否创建索引默认为true
- store:是否存储 默认为false
实体类配置:
/**
* 商品实体类
* @Document() 注解作用:定义一个索引库,一个类型
* indexName属性:指定索引库的名称
* type属性:指定类型名称
* shards属性:指定分片数
* replicas属性:指定复制副本数
*/
@Document(indexName = "ahu4",type = "goods",shards = 5,replicas = 1)
public class Good {
//必须有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;//图片地址
//getter ,setter ,toString
}
测试类:
/**
* 目标:完成创建索引,配置映射
* 1.注入ElasticSearchTemplate对象
* 2.配置Good实体类
* 3.调用创建索引的方法createIndex()
* 调用配置映射的方法PutMapping()
* 测试删除索引方法deleteIndex()
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringdataEsIndex {
//注入ElasticSearchTemplate模板对象
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
//创建索引,配置映射
@Test
public void createIndexAndPutMapping() {
//创建索引
boolean indexResult = elasticsearchTemplate.createIndex(Good.class);
System.out.println("创建索引结果:"+indexResult);
//创建配置映射
elasticsearchTemplate.putMapping(Good.class);
System.out.println("配置映射结果:"+indexResult);
}
//删除索引
@Test
public void deleteIndex(){
elasticsearchTemplate.deleteIndex(Good.class);
}
}
创建结果
2、文档的常见增删改查
继承ElasticsearchRespository模板接口
/**
* 继承持久层接口的ElasticSearch的模板接口
*/
public interface GoodDao extends ElasticsearchRepository<Good,Long> {
}
测试类代码:
/**
* 新增、修改、删除、根据id查询、查询所有、分页查询
* 步骤:
* 1.dao层接口继承ElasticSearchRepository的模板接口
* 2.编写业务层的所有方法
* 3.测试所有方法
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo02DocCURD {
@Autowired
private GoodsDao goodsDao;
//插入文档
@Test
public void save() {
Goods good = Goods.builder().id(1L).title("小米手机").brand("小米").price(19999.0).build();
goodsDao.save(good);
}
//批量新增
@Test
public void saveAll() {
ArrayList<Goods> goods = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Goods good = Goods.builder().id((long) i).title("小米手机").brand("小米").price((double) i).build();
goods.add(good);
}
goodsDao.saveAll(goods);
}
//修改文档
@Test
public void update() {
Goods good = Goods.builder().id(1L).title("小米手机").brand("小米").price(19999.0).images("http://baidu.com/12479122.jpg").build();
goodsDao.save(good);
}
//删除文档
@Test
public void delete() {
Goods good = Goods.builder().id(1L).build();
goodsDao.delete(good);
}
//批量删除
@Test
public void deleteAll() {
goodsDao.deleteAll();
}
//根据id查询
//Optional是对所有pojo的封装对象
//jackson的反序列化需要使用无参构造函数,添加一个无参构造函数就可以了
@Test
public void findById() {
Optional<Goods> goods = goodsDao.findById(2L);
Goods goods1 = goods.get();
System.out.println(goods1);
}
//查询所有
@Test
public void findAll(){
Iterable<Goods> goods = goodsDao.findAll();
for (Goods good : goods) {
System.out.println(good);
}
}
//分页查询
@Test
public void findByPage(){
//设置排序
Sort sort = new Sort(Sort.Direction.DESC,"id");
int currentPage = 2;
int pageSize = 3;
//设置分页查询
PageRequest pageRequest = PageRequest.of(currentPage, pageSize, sort);
//分页查询
Page<Goods> goodsPage = goodsDao.findAll(pageRequest);
for (Goods goods : goodsPage.getContent()) {
System.out.println(goods);
}
}
}
3、Search查询
ElasticSearch的search方法中QueryBuilders,就是原始API查询对象构建对象QueryBuilders。QueryBuilders具备的能力,search方法都具备。
/**
* 目标:搜索
* term查询、match查询,match_all查询...
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo03Search {
@Autowired
private GoodsDao goodsDao;
//term查询
@Test
public void term() {
Iterable<Goods> goods = goodsDao.search(QueryBuilders.termQuery("title", "小米"));
for (Goods good : goods) {
System.out.println(good);
}
}
//terms查询
@Test
public void terms() {
Iterable<Goods> goods = goodsDao.search(QueryBuilders.termsQuery("title", "小米","手机"));
for (Goods good : goods) {
System.out.println(good);
}
}
//multimatch查询
@Test
public void multiMatch() {
Iterable<Goods> goods = goodsDao.search(QueryBuilders.multiMatchQuery("苹果", "title","brand"));
for (Goods good : goods) {
System.out.println(good);
}
}
//match查询
@Test
public void match() {
Iterable<Goods> goods = goodsDao.search(QueryBuilders.matchQuery("title", "苹果"));
for (Goods good : goods) {
System.out.println(good);
}
}
//match精确查询
@Test
public void matchExaxt() {
Iterable<Goods> goods = goodsDao.search(QueryBuilders.matchQuery("title", "苹果手机").operator(Operator.AND));
for (Goods good : goods) {
System.out.println(good);
}
}
//matchAll查询
@Test
public void matchAll() {
Iterable<Goods> goods = goodsDao.search(QueryBuilders.matchAllQuery());
for (Goods good : goods) {
System.out.println(good);
}
}
//分页和排序
@Test
public void pageAndSort() {
//设置排序
Sort sort = new Sort(Sort.Direction.DESC,"id");
int currentPage = 2;
int pageSize = 3;
//设置分页查询
PageRequest pageRequest = PageRequest.of(currentPage, pageSize, sort);
//分页查询
Page<Goods> goodsPage = goodsDao.findAll(pageRequest);
for (Goods goods : goodsPage.getContent()) {
System.out.println(goods);
}
}
//布尔查询
@Test
public void boolQuery() {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("title","小米"));
boolQueryBuilder.mustNot(QueryBuilders.matchQuery("title","电视"));
boolQueryBuilder.should(QueryBuilders.matchQuery("title","手机"));
Iterable<Goods> goods = goodsDao.search(boolQueryBuilder);
for (Goods good : goods) {
System.out.println(good);
}
}
//范围查询
@Test
public void rangeQuery() {
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price");
rangeQueryBuilder.lt(5000);
rangeQueryBuilder.gt(2000);
Iterable<Goods> goods = goodsDao.search(rangeQueryBuilder);
for (Goods good : goods) {
System.out.println(good);
}
}
//模糊查询
@Test
public void fuzzyQuery() {
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("title", "appla手机");
Iterable<Goods> goods = goodsDao.search(fuzzyQueryBuilder);
for (Goods good : goods) {
System.out.println(good);
}
}
}
4、自定义方法名称查询
GoodsRepository提供了非常强大的自定义查询功能;只要遵循SpringData提供的语法,我们可以任意定义方法声明;
查询语法:findBy+字段名+Keyword+字段名+…
Keyword | Sample | Elasticsearch Query String |
---|---|---|
And | findByNameAndPrice | {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Or | findByNameOrPrice | {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Is | findByName | {"bool" : {"must" : {"field" : {"name" : "?"}}}} |
Not | findByNameNot | {"bool" : {"must_not" : {"field" : {"name" : "?"}}}} |
Between | findByPriceBetween | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
Before | findByPriceBefore | {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
After | findByPriceAfter | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Like | findByNameLike | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
StartingWith | findByNameStartingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
EndingWith | findByNameEndingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}} |
In | findByNameIn(Collection<String>names) | {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} |
NotIn | findByNameNotIn(Collection<String>names) | {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} |
OrderBy | findByNameOrderByNameDesc | {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"name" : "?"}}} |
持久层接口:
/**
* ElasticsearchRepository 持久层操作ElasticSearch的模板接口
*/
public interface GoodsDao extends ElasticsearchRepository<Goods, Long> {
//根据商品的标题和加个查询所有的商品信息
List<Goods> findAllByTitleAndPrice(String title, Double price);
}
测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo04CustomMethodNameQuery {
@Autowired
private GoodsDao goodsDao;
@Test
public void findAllByTitleAndPrice(){
List<Goods> goods = goodsDao.findAllByTitleAndPrice("小米", 8.0);
for (Goods good : goods) {
System.out.println(good);
}
}
}