Spring Data 框架介绍

Spring Data 是一个用于简化数据库、非关系型数据库、索引库访问,并支持云服务的开源框架。

  • 其主要目标是使得对数据的访问变得方便快捷,并支持 map-reduce 框架和云计算数据服务。

  • Spring Data 是的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如 MySQL),还是非关系数据库(如 Redis),或者类似 Elasticsearch 这样的索引数据库。从而简化开发人员的代码,提高开发效率。

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

  • Spring Data 常用的功能模块如下:

    ![](6-框架集成 Elasticsearch/9d897933d7d057dc52487ef6f6d1bee7.png)

Spring Data Elasticsearch 介绍

特征

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

版本对比

  • Elasticsearch 和 SpringBoot 之间版本关系要适配,否则 springboot 找不到 elasticsearch 的节点

  • Spring boot2.3.x 一般可以兼容 Elasticsearch7.x

  • 既然我们要用到 Elasticsearch7.x版本,如何选择合适的版本,这里有个小技巧分享给大家。

    • 首先我们可以在 pom.xml 中修改 SpringBoot 依赖的版本为 2.3.11;

    • 然后在项目的External Libraries中搜索elasticsearch,可以发现elasticsearch-7.6.2.jar这个依赖;

    • 然后打开其中的MANIFEST.MF文件,通过 jar 包中的X-Compile-Elasticsearch-Version属性,我们可以找到兼容的 Elasticsearch 版本号为 7.6.2;
      ![](6-框架集成 Elasticsearch/4fc41bbad52fe724c3563f7a3152e604.png)

  • 如果使用最新 springboot 或 Elasticsearch,我们排除 spring 对 es 的依赖后,手动引入期望的 es 依赖,通过自己编写 Configuration 类和配置项来得到 HighLevelRestClient 这个 bean, 后续使用的时候直接注入即可

框架集成

创建项目

  • 创建 Maven 项目 es-springdata

  • 修改 pom 文件,增加依赖关系

    • 目前最新 springboot 对应 Elasticsearch7.6.2
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <spring-boot.version>2.3.11.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <exclusions>
    <!-- 删除 JUnit 4 的测试引擎-->
    <exclusion>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    </exclusion>
    </exclusions>
    <scope>test</scope>
    </dependency>

    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <!--防止将此依赖传递到其它模块中:当其它项目通过 pom 引入该项目时,就不会将该项目中的这个依赖被传递依赖引入进去
    当你依赖某各工程很庞大或很可能与其他工程的 jar 包冲突的时候建议加上该选项,可以节省开销,同时减少依赖冲突-->
    <optional>true</optional>
    </dependency>

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <!-- 防止将此依赖传递到其它模块中-->
    <optional>true</optional>
    </dependency>
    </dependencies>

    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>${spring-boot.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>
  • SpringBoot 主程序

    1
    2
    3
    4
    # es 服务地址 elasticsearch.host=127.0.0.1 # es 服务端口 elasticsearch.port=9200
    # 配置日志级别,开启 debug 日志
    logging.level.com.atguigu.es=debug

  • 数据实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class Product {
    private Long id; // 商品唯一标识
    private String title; // 商品名称
    private String category; // 分类名称
    private Double price; // 商品价格
    private String images; // 图片地址
    }

配置文件

  • 增加配置文件:在 resources 目录中增加 application.yml 文件

    1
    2
    3
    4
    5
    6
    7
    spring:
    data:
    elasticsearch:
    # es 的集群名称
    cluster-name: myes
    # es 的连接地址及端口号
    cluster-nodes: zy01:9001,zy02:9002,zy03:9003
  • 上面配置 Elasticsearch 访问路径和集群名称的配置已经不建议使用了;取而代之的是直接配置 Elasticsearch 的 rest 访问地址

    1
    2
    3
    4
    spring:
    elasticsearch:
    rest:
    uris: http://localhost:9200

配置类

  • ElasticsearchRestTemplate 是 spring-data-elasticsearch 项目中的一个类,和其他 spring 项目中的 template 类似。

  • 在新版的 spring-data-elasticsearch 中,ElasticsearchRestTemplate 代替了原来的 ElasticsearchTemplate。

  • 原因是 ElasticsearchTemplate 基于 TransportClient,TransportClient 即将在 8.x 以后的版本中移除。所以,我们推荐使用 ElasticsearchRestTemplate。

  • ElasticsearchRestTemplate 基于 RestHighLevelClient 客户端的。

  • 自定义配置类,继承 AbstractElasticsearchConfiguration,并实现 elasticsearchClient() 抽象方法,创建 RestHighLevelClient 对象。

    1
    2
    3
    4
    5
    6
    elasticsearch:
    # es 服务地址
    host: 127.0.0.1
    # es 服务端口
    port: 9200
    # cluster-nodes: zy01:9001,zy02:9002,zy03:9003
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Data
    @Configuration
    @ConfigurationProperties(prefix = "elasticsearch")
    public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
    private String host;
    private Integer port;

    // 重写父类方法
    @Override
    public RestHighLevelClient elasticsearchClient() {
    RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
    return new RestHighLevelClient(builder);
    }
    }

属性映射字段

Spring Data 通过注解来声明字段的映射属性,有下面的三个注解:

  • @Document 作用在类,标记实体类为文档对象,一般有两个属性

    • indexName:对应索引库名称
    • type:对应在索引库中的类型
    • shards:分片数量,默认 5
    • replicas:副本数量,默认 1
  • @Id 作用在成员变量,标记一个字段作为 id 主键

  • @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:

    • type:字段类型,是枚举:FieldType,可以是 text、long、short、date、integer、object 等
      • text:存储数据时候,会自动分词,并生成索引
      • keyword:存储数据时候,不会分词建立索引
      • Numerical:数值类型,分两类
      • 基本数据类型:long、interger、short、byte、double、float、half_float
      • 浮点数的高精度类型:scaled_float 需要指定一个精度因子,比如 10 或 100。elasticsearch 会把真实值乘以这个因子后存储,取出时再还原。
      • Date:日期类型
      • elasticsearch 可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为 long,节省空间。
      • index:是否索引,布尔类型,默认是 true
      • store:是否存储,布尔类型,默认是 false
      • analyzer:分词器名称,这里的 ik_max_word 即使用 ik 分词器
  • 实体类映射操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    @Document(indexName = "shopping", shards = 3, replicas = 1)
    public class Product {
    // 必须有 id, 这里的 id 是全局唯一的标识,等同于 es 中的"_id"
    @Id private Long id; // 商品唯一标识

    @MultiField(
    mainField = @Field(type = FieldType.Text, analyzer = "ik_max_word"),
    otherFields = {
    @InnerField(suffix = "inner", type = FieldType.Keyword),
    @InnerField(suffix = "keyword", type = FieldType.Keyword)
    })
    private String title; // 商品名称

    @Field(type = FieldType.Keyword)
    private String category; // 分类名称

    @Field(type = FieldType.Double)
    private Double price; // 商品价格

    @Field(type = FieldType.Keyword, index = false)
    private String images; // 图片地址
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    # 对应的映射为
    {
    "mappings": {
    "_doc": {
    "properties": {
    "images": {
    "index": false,
    "type": "keyword"
    },
    "price": {
    "type": "double"
    },
    "_class": {
    "type": "text",
    "fields": {
    "keyword": {
    "ignore_above": 256,
    "type": "keyword"
    }
    }
    },
    "id": {
    "type": "long"
    },
    "category": {
    "type": "keyword"
    },
    "title": {
    "analyzer": "ik_max_word",
    "type": "text",
    "fields": {
    "keyword": {
    "type": "keyword"
    },
    "inner": {
    "type": "keyword"
    }
    }
    }
    }
    }
    }
    }

DAO 访问

  • DAO 数据访问对象

    1
    2
    3
    4
    @Repository
    public interface ProductDao extends ElasticsearchRepository<Product,Long> {
    }

elasticsearchRestTemplate

索引操作

1
2
3
4
5
6
// 索引创建
boolean isCreate = restTemplate.indexOps(IndexCoordinates.of("template")).create();
// 获取索引
boolean exists = restTemplate.indexOps(IndexCoordinates.of("template")).exists();
// 删除索引
boolean delete = restTemplate.indexOps(IndexCoordinates.of("template")).delete();

文档操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 判断文档是否存在
boolean exists = elasticsearchRestTemplate.exists("26", PmsProduct.class);
boolean exists = elasticsearchRestTemplate.exists("26", IndexCoordinates.of("pms_bak"));

// 保存文档
elasticsearchRestTemplate.save(pmsProduct);
elasticsearchRestTemplate.save(pmsProductNo, IndexCoordinates.of("pms_bak"));

// 修改文档
Document document = Document.create();
document.append("name", "haha");
UpdateQuery updateQuery = UpdateQuery.builder("2").withDocument(document).build();
UpdateResponse pms_bak1 = elasticsearchRestTemplate.update(updateQuery, IndexCoordinates.of("pms_bak"));

// 删除文档
String template = elasticsearchRestTemplate.delete("1", IndexCoordinates.of("pms"));

// 根据复杂条件删除【多条】【方法一】
StringQuery name = new StringQuery(QueryBuilders.matchQuery("name", "我").toString());
elasticsearchRestTemplate.delete(name, null, IndexCoordinates.of("pms_bak"));

// 根据复杂条件删除【多条】【方法二】
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "华为");
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder().withQuery(matchQueryBuilder).build();
elasticsearchRestTemplate.delete(nativeSearchQuery, null, IndexCoordinates.of("pms_bak"));

// 保存多条文档
List<IndexQuery> indexQueryList = new ArrayList<>();
IndexQuery indexQuery;
for (int i = 0; i < 10; i++) {
PmsProductBak pmsProductBak = new PmsProductBak();
pmsProductBak.setId(Long.valueOf(i));
pmsProductBak.setName("我是爸爸" + i);
indexQuery = new IndexQuery();
indexQuery.setObject(pmsProductBak);
indexQueryList.add(indexQuery);
}
List<String> list = elasticsearchRestTemplate.bulkIndex(indexQueryList, BulkOptions.defaultOptions(), IndexCoordinates.of("pms_bak"));

ElasticsearchRepository

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.data.repository;

import java.util.Optional;

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S var1);

<S extends T> Iterable<S> saveAll(Iterable<S> var1);

Optional<T> findById(ID var1);

boolean existsById(ID var1);

Iterable<T> findAll();

Iterable<T> findAllById(Iterable<ID> var1);

long count();

void deleteById(ID var1);

void delete(T var1);

void deleteAll(Iterable<? extends T> var1);

void deleteAll();
}

  • 构建方法关键字的使用

    关键字 使用示例 等同于的 ES 查询
    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}}}}}
    LessThanEqual findByPriceLessThan {“bool” : {“must” : {“range” : {“price” : {“from” : null,”to” : ?,”include_lower” : true,”include_upper” : true}}}}}
    GreaterThanEqual findByPriceGreaterThan {“bool” : {“must” : {“range” : {“price” : {“from” : ?,”to” : null,”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}}}}}
    Contains/Containing findByNameContaining {“bool” : {“must” : {“field” : {“name” : {“query” : “?”,”analyze_wildcard” : true}}}}}
    In findByNameIn(Collectionnames) {“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“name” : “?”}} ]}}}}
    NotIn findByNameNotIn(Collectionnames) {“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?”}}}}}}
    True findByAvailableTrue {“bool” : {“must” : {“field” : {“available” : true}}}}
    False findByAvailableFalse {“bool” : {“must” : {“field” : {“available” : false}}}}
    OrderBy findByAvailableTrueOrderByNameDesc {“sort” : [{ “name” : {“order” : “desc”} }],”bool” : {“must” : {“field” : {“available” : true}}}}

使用

索引操作

  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package pers.fulsun.es_springdata;

    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
    import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
    import pers.fulsun.es_springdata.entity.Product;

    @SpringBootTest
    public class SpringDataESIndexTest {
    // 注入 ElasticsearchRestTemplate
    @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /** 创建索引并增加映射配置 */
    @Test
    // @Disabled
    public void createIndex() {
    // 创建索引,系统初始化会自动创建索引
    System.out.println("创建文档索引");
    boolean isCreate = elasticsearchRestTemplate.indexOps(IndexCoordinates.of("template")).create();
    System.out.println("创建 template 索引" + isCreate);
    }

    @Test
    public void getIndex() {
    // 获取索引
    boolean exists = elasticsearchRestTemplate.indexOps(IndexCoordinates.of("template")).exists();
    System.out.println("template 索引是否存在 = " + exists);
    }

    @Test
    public void deleteIndex() {
    // 创建索引,系统初始化会自动创建索引
    // boolean flg = elasticsearchRestTemplate.deleteIndex(Product.class);
    boolean flg = elasticsearchRestTemplate.indexOps(Product.class).delete();
    System.out.println("删除索引 = " + flg);
    }
    }

文档操作

  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    @SpringBootTest
    public class SpringDataESProductDaoTest {
    @Autowired private ProductDao productDao;

    /** 新增 */
    @Test
    public void save() {
    Product product = new Product();
    product.setId(1L);
    product.setTitle(" 华 为 手 机 ");
    product.setCategory(" 手 机 ");
    product.setPrice(2999.0);
    product.setImages("http://www.shouji.com/hw.jpg");
    productDao.save(product);
    }

    // 修改:不存在会插入
    @Test
    public void update() {
    Product product = new Product();
    product.setId(1L);
    product.setTitle("小米 2 手机");
    product.setCategory(" 手 机 ");
    product.setPrice(9999.0);
    product.setImages("http://www.shouji.com/xm.jpg");
    productDao.save(product);
    }

    // 根据 id 查询
    @Test
    public void findById() {
    Product product = productDao.findById(1L).get();
    System.out.println(product);
    }

    // 查询所有
    @Test
    public void findAll() {
    Iterable<Product> products = productDao.findAll();
    for (Product product : products) {
    System.out.println(product);
    }
    }

    // 删除
    @Test
    public void delete() {
    Product product = new Product();
    product.setId(1L);
    productDao.delete(product);
    }

    // 批量新增
    @Test
    public void saveAll() {
    List<Product> productList = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
    Product product = new Product();
    product.setId(Long.valueOf(i));
    product.setTitle("[" + i + "] 小 米 手 机 ");
    product.setCategory(" 手 机 ");
    product.setPrice(1999.0 + i);
    product.setImages("http://www.shouji.com/xm.jpg");
    productList.add(product);
    }
    productDao.saveAll(productList);
    }

    // 分页查询
    @Test
    public void findByPageable() {
    // 设置排序(排序方式,正序还是倒序,排序的 id)
    Sort sort = Sort.by(Direction.ASC, "id");
    int currentPage = 0; // 当前页,第一页从 0 开始,1 表示第二页
    int pageSize = 5; // 每页显示多少条
    // 设置查询分页
    PageRequest pageRequest = PageRequest.of(currentPage, pageSize, sort);
    // 分页查询
    Page<Product> productPage = productDao.findAll(pageRequest);
    for (Product Product : productPage.getContent()) {
    System.out.println(Product);
    }
    }
    }

文档搜索

  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    @SpringBootTest
    public class SpringDataESSearchTest {
    @Autowired private ProductDao productDao;

    /** term 查询 search(termQueryBuilder) 调用搜索方法,参数查询构建器对象 */
    @Test
    public void termQuery() {
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "小米手机");
    Iterable<Product> products = productDao.search(termQueryBuilder);
    for (Product product : products) {
    System.out.println(product);
    }
    }

    /** term 查询加分页 */
    @Test
    public void termQueryByPage() {
    int currentPage = 0;
    int pageSize = 5;
    // 设置查询分页
    PageRequest pageRequest = PageRequest.of(currentPage, pageSize);
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "小米");
    Iterable<Product> products = productDao.search(termQueryBuilder, pageRequest);
    for (Product product : products) {
    System.out.println(product);
    }
    }
    }

NativeSearchQuery

  • 我们发现 ElasticsearchRepository 的search()方法也过时了,不建议使用了,我们可以改用 ElasticsearchRestTemplate 的 search() 方法来实现,具体实现对比如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    @SpringBootTest
    public class SpringDataESSearchTest {
    @Autowired private ProductDao productDao;

    /** term 查询 search(termQueryBuilder) 调用搜索方法,参数查询构建器对象 */
    @Test
    public void termQuery() {
    TermQueryBuilder termQuery = QueryBuilders.termQuery("title.keyword", "小米手机");
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    NativeSearchQuery query = builder.withQuery(termQuery).build();
    SearchHits<Product> searchHits = elasticsearchRestTemplate.search(query, Product.class);
    List<Product> searchProductList =
    searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
    for (Product product : searchProductList) {
    System.out.println(product);
    }
    }

    /** match 查询+排序+分页 */
    @Test
    public void termQueryByPage() {
    int currentPage = 0;
    int pageSize = 5;
    // 设置查询分页
    PageRequest pageRequest = PageRequest.of(currentPage, pageSize);
    NativeSearchQuery nativeSearchQuery =
    new NativeSearchQueryBuilder()
    // 查询条件
    .withQuery(QueryBuilders.matchQuery("title","米"))
    // 分页
    .withPageable(pageRequest)
    // 排序
    .withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC))
    // 高亮字段显示
    .withHighlightFields(new HighlightBuilder.Field("title"))
    .build();
    log.info("\nDSL: {}\n===========================",nativeSearchQuery.getQuery().toString());
    SearchHits<Product> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Product.class);
    searchHits.stream().forEach(item -> System.out.println(item.toString()));
    }
    }

    =======执行结果====================
    DSL: {
    "match" : {
    "title" : {
    "query" : "米",
    "operator" : "OR",
    "prefix_length" : 0,
    "max_expansions" : 50,
    "fuzzy_transpositions" : true,
    "lenient" : false,
    "zero_terms_query" : "NONE",
    "auto_generate_synonyms_phrase_query" : true,
    "boost" : 1.0
    }
    }
    }
    ===========================
    SearchHit{id='9', score=NaN, sortValues=[9], content=Product(id=9, title=[9] 小 米 手 机 , category= 手 机 , price=2008.0, images=http://www.shouji.com/xm.jpg), highlightFields={title=[[9] 小 <em>米</em> 手 机]}}
    SearchHit{id='8', score=NaN, sortValues=[8], content=Product(id=8, title=[8] 小 米 手 机 , category= 手 机 , price=2007.0, images=http://www.shouji.com/xm.jpg), highlightFields={title=[[8] 小 <em>米</em> 手 机]}}
    SearchHit{id='7', score=NaN, sortValues=[7], content=Product(id=7, title=[7] 小 米 手 机 , category= 手 机 , price=2006.0, images=http://www.shouji.com/xm.jpg), highlightFields={title=[[7] 小 <em>米</em> 手 机]}}
    SearchHit{id='6', score=NaN, sortValues=[6], content=Product(id=6, title=[6] 小 米 手 机 , category= 手 机 , price=2005.0, images=http://www.shouji.com/xm.jpg), highlightFields={title=[[6] 小 <em>米</em> 手 机]}}
    SearchHit{id='5', score=NaN, sortValues=[5], content=Product(id=5, title=[5] 小 米 手 机 , category= 手 机 , price=2004.0, images=http://www.shouji.com/xm.jpg), highlightFields={title=[[5] 小 <em>米</em> 手 机]}}

CriteriaQuery

  • API 实例应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  /** CriteriaQuery */
@Test
public void termQueryByPage2() {
int currentPage = 0;
int pageSize = 1;
// 设置查询分页
PageRequest pageRequest = PageRequest.of(currentPage, pageSize);
// 排序
Sort sort = Sort.by(Direction.ASC, "id");
// 高亮字段显示
HighlightBuilder highlightBuilder = new HighlightBuilder();
// 设置标签前缀
highlightBuilder.preTags("<font color='red'>");
// 设置标签后缀
highlightBuilder.postTags("</font>");
// 设置高亮字段
highlightBuilder.field("name");
HighlightQuery highlightQuery = new HighlightQuery(highlightBuilder);
CriteriaQuery criteriaQuery = new CriteriaQuery( // 查询条件
new Criteria()
.and(new Criteria("title").contains("米"))
.and(new Criteria("price").greaterThanEqual(1000).lessThanEqual(2000)));
criteriaQuery
.setPageable(pageRequest)
.addSort(sort)
.setHighlightQuery(highlightQuery);
SearchHits<Product> searchHits = elasticsearchRestTemplate.search(criteriaQuery, Product.class);
searchHits.stream().forEach(item -> System.out.println(item.toString()));
}
}