写在前面:1. ES及ik分词插件的安装参考上篇文章,ES安装(Linux);2. Springboot(2.1.3.RELEASE)、ES(6.5.0)、spring-boot-starter-data-jest(3.2.5.RELEASE)三者版本的对应关系,因为版本问题调整了6个小时才无异常运行。
一、ES客户端
Elasticsearch(ES)提供了两种客户端连接方式:
- transport :通过 TCP 方式访问 ES 。
- rest :通过 HTTP API 方式访问 ES 。
在项目中,编写数据库操作的逻辑,使用 MyBatis 或者 JPA 为主,而不使用原生的 JDBC 。那么,我们在编写 Elasticsearch 操作的逻辑,也不直接使用上述的客户端,而是:
-
spring-data-elasticsearch
,基于 Elasticsearch transport 客户端封装。 -
spring-data-jest
,基于 Jest 客户端封装。
虽然这两者底层使用的不同客户端,但是都基于 Spring Data 体系,所以项目在使用时,编写的代码是相同的。也因此,如果想从 spring-data-elasticsearch
迁移到 spring-data-jest
时,基本无成本。
二、背景故事
虽然说,ES 提供了 2 种方式,官方目前建议使用 rest 方式,而不是 transport 方式。并且,transport 在未来的计划中,准备废弃。并且,阿里云提供的 Elasticsearch 更加干脆,直接只提供 rest 方式,而不提供 transport 方式。
在社区中,有个 Jest 开源项目,提供了的 Elasticsearch REST API 客户端。
重要结论:所以,为了兼容我们需要使用spring-data-jest。
三、Spring Data Jest
本文重点是使用spring-data-jest方式跟ES打交道。我们会使用 spring-boot-starter-data-jest
自动化配置 Spring Data Jest 主要配置。同时,编写相应的 Elasticsearch 的 CRUD 操作。
3.1 引入依赖
在 [pom.xml
]文件中,引入相关依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>es-spring-data-jest</artifactId>
<dependencies>
<!-- 自动化配置 Spring Data Jest -->
<dependency>
<groupId>com.github.vanroy</groupId>
<artifactId>spring-boot-starter-data-jest</artifactId>
<version>3.2.5.RELEASE</version>
</dependency>
<!-- 方便写单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>spring-boot-autoconfigure</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
3.2 Application启动类
package cn.erbadagang.springboot.es.springdatajest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration;
/**
* @description ES Jest客户端项目启动类。需要排除 ElasticsearchAutoConfiguration 和 ElasticsearchDataAutoConfiguration 自动配置类,否则会自动配置 Spring Data Elasticsearch 。
* @ClassName: Application
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/5 10:27
* @Copyright:
*/
@SpringBootApplication(exclude = {ElasticsearchAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
需要排除 ElasticsearchAutoConfiguration 和 ElasticsearchDataAutoConfiguration 自动配置类,否则会自动配置 Spring Data Elasticsearch 。
3.3 配置文件
在 [application.yml
]中,添加 Jest 配置,如下:
spring:
data:
# Jest 配置项
jest:
uri: http://127.0.0.1:9200
- 我们使用本地的 ES 服务。默认情况下,ES rest 连接方式暴露的端口是 9200 。
3.4 ESProductDO
package cn.erbadagang.springboot.es.springdatajest.dataobject;
import cn.erbadagang.springboot.es.springdatajest.constant.FieldAnalyzer;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* @description
* @ClassName: ESProductDO
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/5 15:16
* @Copyright:
*/
@Document(indexName = "product", // 索引名
type = "product", // 类型。未来的版本即将废弃
shards = 1, // 默认索引分区数
replicas = 0, // 每个分区的备份数
refreshInterval = "-1" // 刷新间隔
)
public class ESProductDO {
/**
* ID 主键
*/
@Id
private Integer id;
/**
* SPU 名字
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String name;
/**
* 卖点
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String sellPoint;
/**
* 描述
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String description;
/**
* 分类编号
*/
private Integer cid;
/**
* 分类名
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String categoryName;
public Integer getId() {
return id;
}
public ESProductDO setId(Integer id) {
this.id = id;
return this;
}
public String getName() {
return name;
}
public ESProductDO setName(String name) {
this.name = name;
return this;
}
public String getSellPoint() {
return sellPoint;
}
public ESProductDO setSellPoint(String sellPoint) {
this.sellPoint = sellPoint;
return this;
}
public String getDescription() {
return description;
}
public ESProductDO setDescription(String description) {
this.description = description;
return this;
}
public Integer getCid() {
return cid;
}
public ESProductDO setCid(Integer cid) {
this.cid = cid;
return this;
}
public String getCategoryName() {
return categoryName;
}
public ESProductDO setCategoryName(String categoryName) {
this.categoryName = categoryName;
return this;
}
@Override
public String toString() {
return "ProductDO{" +
"id=" + id +
", name='" + name + '\'' +
", sellPoint='" + sellPoint + '\'' +
", description='" + description + '\'' +
", cid=" + cid +
", categoryName='" + categoryName + '\'' +
'}';
}
}
- 为了区别关系数据库的实体对象,以 ES 前缀开头。
- 字段上的
@Field
注解的 [FieldAnalyzer],是定义的常量类。代码如下:
package cn.erbadagang.springboot.es.springdatajest.constant;
/**
* ES 字段分析器的常量类
*
* 关于 IK 分词,文章 https://blog.csdn.net/xsdxs/article/details/72853288 不错。
* 目前项目使用的 ES 版本是 6.5.0 ,可以在 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-5-0 下载。
* 如果不知道怎么安装 ES ,可以看 https://www.jianshu.com/p/941c9797923e。
*/
public class FieldAnalyzer {
/**
* IK 最大化分词
*
* 会将文本做最细粒度的拆分
*/
public static final String IK_MAX_WORD = "ik_max_word";
/**
* IK 智能分词
*
* 会做最粗粒度的拆分
*/
public static final String IK_SMART = "ik_smart";
}
一定要记得给 Elasticsearch 安装 IK 插件,不然等会示例会报错。报错信息类似:“unknown index.”
3.5 ProductRepository
创建 [ProductRepository] 接口。代码如下:
package cn.erbadagang.springboot.es.springdatajest.repository;
import cn.erbadagang.springboot.es.springdatajest.dataobject.ESProductDO;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* @description JPA操作ES
* @ClassName: ProductRepository
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/5 15:20
* @Copyright:
*/
public interface ProductRepository extends ElasticsearchRepository<ESProductDO, Integer> {
}
- 继承
org.springframework.data.elasticsearch.repository.ElasticsearchRepository
接口,第一个泛型设置对应的实体是 ESProductDO ,第二个泛型设置对应的主键类型是 Integer 。 - 因为实现了 ElasticsearchRepository 接口,Spring Data Jest 会自动生成对应的 CRUD 等等的代码。
-
ElasticsearchRepository 类图如下:
上图会发现和 Spring Data JPA 操作MySQL数据库的使用方式基本一致。这就是 Spring Data 带给我们的好处,使用相同的 API ,统一访问不同的数据源。
3.6 简单JPA操作ES测试
创建 [ProductRepositoryTest](测试类,我们来测试一下简单的 ProductRepository 的每个操作。代码如下:
package cn.erbadagang.springboot.es.springdatajest.repository;
import cn.erbadagang.springboot.es.springdatajest.Application;
import cn.erbadagang.springboot.es.springdatajest.dataobject.ESProductDO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Optional;
/**
* @description
* @ClassName: ProductRepositoryTest
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/6 9:52
* @Copyright:
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ProductRepositoryTest {
@Autowired
private ProductRepository productRepository;
@Test // 插入一条记录
public void testInsert() {
ESProductDO product = new ESProductDO();
product.setId(1); // 一般 ES 的 ID 编号使用 DB 数据对应的编号。这里先写死
product.setName("简单JPA操作的ES");
product.setSellPoint("简单,跟普通JPA data的语法一致进行ES操作");
product.setDescription("描述:自带CRUD");
product.setCid(1);
product.setCategoryName("技术分类");
productRepository.save(product);
product.setId(5); // 一般 ES 的 ID 编号使用 DB 数据对应的编号。这里先写死
product.setName("ES5");
product.setSellPoint("跟普通JPA data的语法一致进行ES操作");
product.setDescription("描述5:自带CRUD");
product.setCid(5);
product.setCategoryName("技术分类");
productRepository.save(product);
}
// 这里要注意,如果使用 save 方法来更新的话,必须是全量字段,否则其它字段会被覆盖。
// 所以,这里仅仅是作为一个示例。
@Test // 更新一条记录
public void testUpdate() {
ESProductDO product = new ESProductDO();
product.setId(1);
product.setCid(2);
product.setCategoryName("tech-Java");
productRepository.save(product);
}
@Test // 根据 ID 编号,删除一条记录
public void testDelete() {
productRepository.deleteById(1);
}
@Test // 根据 ID 编号,查询一条记录
public void testSelectById() {
Optional<ESProductDO> userDO = productRepository.findById(1);
System.out.println("testSelectById():" + userDO);
}
@Test // 查询所有记录
public void testAll() {
Iterable<ESProductDO> users = productRepository.findAll();
users.forEach(System.out::println);
}
}
部分运行结果:
id=1
的name变成空,是因为后面调用了save 方法来更新,必须是全量字段,否则其它字段会被覆盖。
四、JPA——基于方法名查询
在 Spring Data 中,支持根据方法名作生成对应的查询(WHERE)
条件,进一步进化我们使用 JPA ,具体是方法名以 findBy、existsBy、countBy、deleteBy
开头,后面跟具体的条件。具体的规则:
关键字 | 方法示例 | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is, Equals | findByFirstname, findByFirstnameIs, findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull, Null | findByAge(Is)Null | … where x.age is null |
IsNotNull, NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
注意,如果我们有排序需求,可以使用 OrderBy 关键字。
因为 Spring Data Elasticsearch 和 Spring Data Jest 也是 Spring Data 体系中的一员,所以也能享受到基于方法名查询的福利。下面,我们来编写一个简单的示例。
4.1 ProductRepositoryWithMethodName
package cn.erbadagang.springboot.es.springdatajest.repository;
import cn.erbadagang.springboot.es.springdatajest.dataobject.ESProductDO;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* @description 使用JPA的基于方法名的操作,如: findBy、existsBy、countBy、deleteBy 开头。
* @ClassName: ProductRepositoryWithMethodName
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/6 15:04
* @Copyright:
*/
public interface ProductRepositoryWithMethodName extends ElasticsearchRepository<ESProductDO, Integer> {
ESProductDO findByName(String name);
Page<ESProductDO> findByNameLike(String name, Pageable pageable);
}
对于分页操作,需要使用到Pageable
参数,需要作为方法的最后一个参数。
4.2 简单测试
创建 [ProductRepositoryWithMethodNameTest
]测试类,我们来测试一下简单的 ProductRepositoryWithMethodName
的每个操作。代码如下:
package cn.erbadagang.springboot.es.springdatajest.repository;
import cn.erbadagang.springboot.es.springdatajest.Application;
import cn.erbadagang.springboot.es.springdatajest.dataobject.ESProductDO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @description 使用JPA命名规范自定义的查询测试
* @ClassName: ProductRepositoryWithMethodNameTest
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/6 21:21
* @Copyright:
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ProductRepositoryWithMethodNameTest {
@Autowired
private ProductRepositoryWithMethodName productRepository;
@Test // 根据名字获得一条记录
public void testFindByName() {
ESProductDO product = productRepository.findByName("郭秀志源码");
System.out.println(product);
}
@Test // 使用 name 模糊查询,分页返回结果
public void testFindByNameLike() {
// 根据情况,是否要制造测试数据
if (true) {
testInsert();
}
// 创建排序条件
// Sort sort = new Sort(Sort.Direction.DESC, "id");//废弃的写法
Sort sort = Sort.by(Sort.Direction.DESC, "id");// ID 倒序
// 创建分页条件。
Pageable pageable = PageRequest.of(0, 10, sort);
// 执行分页操作
Page<ESProductDO> page = productRepository.findByNameLike("郭秀志", pageable);
// 打印
System.out.println(page.getTotalElements());
System.out.println(page.getTotalPages());
//遍历所有取出ESProductDO数据
page.get().forEach(System.out::println);
}
/**
* 为了给分页制造一点数据
*/
private void testInsert() {
for (int i = 1; i <= 100; i++) {
ESProductDO product = new ESProductDO();
product.setId(i); // 一般 ES 的 ID 编号使用 DB 数据对应的编号。先写死成数据循环变化
product.setName("郭秀志源码:" + i);
product.setSellPoint("免费开源,不要钱了");
product.setDescription("描述,description");
product.setCid(1);
product.setCategoryName("技术");
productRepository.save(product);
}
}
}
运行结果:五、自定义复杂查询
在一些业务场景下,我们需要编写相对复杂的查询,例如说类似京东 https://search.jd.com/Search?keyword=华为手机 搜索功能,需要支持关键字、分类、品牌等等,并且可以按照综合、销量等等升降序排序,那么我们就无法在使用上面的 Spring Data Repository 提供的简单的查询方法,而需要使用到
ElasticsearchRepository 的
search `方法,代码如下:
// ElasticsearchRepository.java
// 省略非 search 方法
Page<T> search(QueryBuilder query, Pageable pageable);
Page<T> search(SearchQuery searchQuery);
Page<T> searchSimilar(T entity, String[] fields, Pageable pageable);
此时,我们就需要使用QueryBuilder
和SearchQuery
构建相对复杂的搜索和排序条件。
接下来,实现一个简单的商品搜索功能。
5.1 ProductRepositoryNativeSearchQuery
package cn.erbadagang.springboot.es.springdatajest.repository;
import cn.erbadagang.springboot.es.springdatajest.dataobject.ESProductDO;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.util.StringUtils;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
/**
* @description 自定义复杂查询。
* @ClassName: ProductRepositoryNativeSearchQuery
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/7 11:50
* @Copyright:
*/
public interface ProductRepositoryNativeSearchQuery extends ElasticsearchRepository<ESProductDO, Integer> {
default Page<ESProductDO> search(Integer cid, String keyword, Pageable pageable) {
// <1> 创建 NativeSearchQueryBuilder 对象
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
// <2.1> 筛选条件 cid
if (cid != null) {
nativeSearchQueryBuilder.withFilter(QueryBuilders.termQuery("cid", cid));
}
// <2.2> 筛选
if (StringUtils.hasText(keyword)) {
FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = { // TODO 分值随便打的
new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("name", keyword),
ScoreFunctionBuilders.weightFactorFunction(10)),
new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("sellPoint", keyword),
ScoreFunctionBuilders.weightFactorFunction(2)),
new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("categoryName", keyword),
ScoreFunctionBuilders.weightFactorFunction(3)),
// new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("description", keyword),
// ScoreFunctionBuilders.weightFactorFunction(2)), // TODO 目前这么做,如果商品描述很长,在按照价格降序,会命中超级多的关键字。
};
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(functions)
.scoreMode(FunctionScoreQuery.ScoreMode.SUM) // 求和
.setMinScore(2F); // TODO 需要考虑下 score
nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder);
}
// 排序
if (StringUtils.hasText(keyword)) { // <3.1> 关键字,使用打分
nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));
} else if (pageable.getSort().isSorted()) { // <3.2> 有排序,则进行拼接
pageable.getSort().get().forEach(sortField -> nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField.getProperty())
.order(sortField.getDirection().isAscending() ? SortOrder.ASC : SortOrder.DESC)));
} else { // <3.3> 无排序,则按照 ID 倒序
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));
}
// <4> 分页
nativeSearchQueryBuilder.withPageable(PageRequest.of(pageable.getPageNumber(), pageable.getPageSize())); // 避免
// <5> 执行查询
return search(nativeSearchQueryBuilder.build());
}
}
使用 QueryBuilder 和 SearchQuery 构建相对复杂的搜索和排序条件,我们可以放在 Service 层,也可以放在 Repository 层。个人的偏好放在 Repository 层。
主要原因是,尽量避免数据层的操作暴露在 Service 层。
缺点呢,就像我们这里看到的,有点业务逻辑就到了 Repository 层。
有舍有得,看个人喜好。翻了一些开源项目,放在 Service 或 Repository 层的都有。
简单来说下这个方法的整体逻辑,根据商品分类编号 + 关键字,检索相应的商品,分页返回结果。
<1> 处,创建 NativeSearchQueryBuilder 对象。
筛选条件
<2.1> 处,如果有分类编号 cid ,则进行筛选。
<2.2> 处,如果有关键字 keyword ,则按照 name 10 分、sellPoint 2 分、categoryName 3 分,计算求和,筛选至少满足 2 分。
排序条件
<3.1> 处,如果有关键字,则按照打分结果降序。
<3.2> 处,如果有排序条件,则按照该排序即可。
<3.3> 处,如果无排序条件,则按照 ID 编号降序。
分页条件
<4> 处,创建新的 PageRequest 对象,避免 pageable 里原有的排序条件。
执行搜索
<5> 处,调用 #search(SearchQuery searchQuery) 方法,执行 Elasticsearch 搜索。
5.2 测试
创建测试类:
package cn.erbadagang.springboot.es.springdatajest.repository;
import cn.erbadagang.springboot.es.springdatajest.Application;
import cn.erbadagang.springboot.es.springdatajest.dataobject.ESProductDO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @description 自定义NativeSearchQuery测试类
* @ClassName: ProductRepositoryNativeSearchQueryTest
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/7 11:53
* @Copyright:
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ProductRepositoryNativeSearchQueryTest {
@Autowired
private ProductRepositoryNativeSearchQuery productRepository;
@Test
public void testSearch() {
// 查找分类为 1 + 指定关键字,并且按照 id 升序
Page<ESProductDO> page = productRepository.search(1, "技术",
PageRequest.of(0, 5, Sort.Direction.ASC, "id"));
System.out.println(page.getTotalPages());
//输出查询结果。
page.forEach(System.out::println);
// 查找分类为 1 ,并且按照 id 升序
page = productRepository.search(1, null,
PageRequest.of(0, 5, Sort.Direction.ASC, "id"));
System.out.println(page.getTotalPages());
//输出查询结果。
page.forEach(System.out::println);
}
}
运行结果:底线
本文源代码使用 Apache License 2.0开源许可协议,可从Gitee代码地址通过git clone
命令下载到本地或者通过浏览器方式查看源代码。