Spring-Boot使用rest方式整合Elasticsearch

写在前面: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]文件中,引入相关依赖。



    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.3.RELEASE
         
    
    4.0.0

    es-spring-data-jest

    
        
        
            com.github.vanroy
            spring-boot-starter-data-jest
            3.2.5.RELEASE
        

        
        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    spring-boot-autoconfigure
                    org.springframework.boot
                
            
        

    


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: 郭秀志 [email protected]
 * @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: 郭秀志 [email protected]
 * @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: 郭秀志 [email protected]
 * @date: 2020/7/5 15:20
 * @Copyright:
 */
public interface ProductRepository extends ElasticsearchRepository {

}

  • 继承 org.springframework.data.elasticsearch.repository.ElasticsearchRepository 接口,第一个泛型设置对应的实体是 ESProductDO ,第二个泛型设置对应的主键类型是 Integer 。
  • 因为实现了 ElasticsearchRepository 接口,Spring Data Jest 会自动生成对应的 CRUD 等等的代码。
  • ElasticsearchRepository 类图如下:
    Spring-Boot使用rest方式整合Elasticsearch_第1张图片
    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: 郭秀志 [email protected]
 * @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 userDO = productRepository.findById(1);
        System.out.println("testSelectById():" + userDO);
    }

    @Test // 查询所有记录
    public void testAll() {
        Iterable users = productRepository.findAll();
        users.forEach(System.out::println);
    }

}

部分运行结果:

Spring-Boot使用rest方式整合Elasticsearch_第2张图片
junit运行结果

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: 郭秀志 [email protected]
 * @date: 2020/7/6 15:04
 * @Copyright:
 */
public interface ProductRepositoryWithMethodName extends ElasticsearchRepository {

    ESProductDO findByName(String name);

    Page 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: 郭秀志 [email protected]
 * @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 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);
        }
    }

}

运行结果:
Spring-Boot使用rest方式整合Elasticsearch_第3张图片
测试结果

五、自定义复杂查询

在一些业务场景下,我们需要编写相对复杂的查询,例如说类似京东 https://search.jd.com/Search?keyword=华为手机 搜索功能,需要支持关键字、分类、品牌等等,并且可以按照综合、销量等等升降序排序,那么我们就无法在使用上面的 Spring Data Repository 提供的简单的查询方法,而需要使用到 ElasticsearchRepository search `方法,代码如下:

// ElasticsearchRepository.java
// 省略非 search 方法

Page search(QueryBuilder query, Pageable pageable);

Page search(SearchQuery searchQuery);

Page searchSimilar(T entity, String[] fields, Pageable pageable);

此时,我们就需要使用QueryBuilderSearchQuery构建相对复杂的搜索和排序条件。

接下来,实现一个简单的商品搜索功能。

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: 郭秀志 [email protected]
 * @date: 2020/7/7 11:50
 * @Copyright:
 */
public interface ProductRepositoryNativeSearchQuery extends ElasticsearchRepository {

    default Page 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: 郭秀志 [email protected]
 * @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 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);
    }

}

运行结果:
Spring-Boot使用rest方式整合Elasticsearch_第4张图片
自定义SearchQuery运行结果

底线


本文源代码使用 Apache License 2.0开源许可协议,可从Gitee代码地址通过git clone命令下载到本地或者通过浏览器方式查看源代码。

你可能感兴趣的:(Spring-Boot使用rest方式整合Elasticsearch)