• 6.5 Elasticsearch(五)Spring Data Elasticsearch - 增删改查API




    1.Spring Data Elasticsearch

    可以访问spring官网查看其API的具体使用方式:https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference
    在这里插入图片描述

    Spring Data ElasticsearchElasticsearch 搜索引擎开发的解决方案。它提供了模板对象,用于存储、搜索、排序文档和构建聚合的高级API。

    例如,Repository 使开发者能够通过定义具有自定义方法名称的接口来表达查询。

    2.案例准备

    Elasticsearch 中存储学生数据,并对学生数据进行搜索测试。

    数据结构:

    学号姓名性别出生日期
    27张三2020-12-4

    案例测试以下数据操作:

    2.1 在 Elasticsearch 中创建 students 索引

    在开始运行测试之前,在 Elasticsearch 中先创建 students 索引:

    PUT /students
    {
      "settings": {
        "number_of_shards": 3,
        "number_of_replicas": 2,
        "index.max_ngram_diff":30,
        "analysis": {
          "analyzer": {
            "ngram_analyzer": {
              "tokenizer": "ngram_tokenizer"
            }
          },
          "tokenizer": {
            "ngram_tokenizer": {
              "type": "ngram",
              "min_gram": 1,
              "max_gram": 30,
              "token_chars": [
                "letter",
                "digit"
              ]
            }
          }
        }
      },
      "mappings": {
        "properties": {
          "id": {
            "type": "long"
          },
          "name": {
            "type": "text",
            "analyzer": "ngram_analyzer"
          },
          "gender": {
            "type": "keyword"
          },
          "birthDate": {
            "type": "date",
            "format": "yyyy-MM-dd"
          }
        }
      }
    }
    
    • 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

    创建索引之后,kibana容器就不用使用了,因为其占用了比较多的内容,所以可以使用docker rm -f kibana删除kibana容器;
    在这里插入图片描述

    2.2 案例测试说明

    通过对学生数据的CRUD操作,来使用Spring Data ElasticsearchAPI:

    1. C - 创建学生数据
    2. R - 访问学生数据
    3. U - 修改学生数据
    4. D - 删除学生数据
    5. 使用 Repository 和 Criteria 搜索学生数据

    3.创建项目

    3.1 新建工程

    我们新建一个新的elasticsearchEmpty Project工程:
    在这里插入图片描述

    3.2 新建 springboot module,添加 spring data elasticsearch 依赖

    在这里插入图片描述

    3.3 pom.xml 文件

    
    <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">
        <modelVersion>4.0.0modelVersion>
    
        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>2.3.6.RELEASEversion>
            <relativePath/> 
        parent>
    
        <groupId>cn.studygroupId>
        <artifactId>spring-data-esartifactId>
        <version>1.0-SNAPSHOTversion>
    
        <properties>
            <java.version>1.8java.version>
        properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-elasticsearchartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintagegroupId>
                        <artifactId>junit-vintage-engineartifactId>
                    exclusion>
                exclusions>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
            dependency>
        dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                plugin>
            plugins>
        build>
    
    project>
    
    • 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

    3.4 application.yml 配置

    #logging.level.tracer=TRACE 作用是在控制台中显示底层的查询日志
    spring:
      elasticsearch:
        rest:
          uris:
            - http://192.168.64.181:9200
            - http://192.168.64.181:9201
            - http://192.168.64.181:9202
    # REST API调用的http协议数据日志
    logging:
      level:
        tracer: TRACE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.Student 实体类

    package cn.study.entity;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import org.springframework.data.annotation.Id;
    import org.springframework.data.elasticsearch.annotations.Document;
    import org.springframework.data.elasticsearch.annotations.Field;
    
    /**
     * spring data es API 可以根据这里的设置,
     * 在服务器上自动创建索引,
     * 一般索引需要自己手动创建,不应依赖于客户端API自动创建
     * indexName:索引名,我们这里对应的是students
     * shards:可选属性,分片数量
     * replicas:可选属性,副本数量
     */
    @Document(indexName = "students",shards = 3,replicas = 2)
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Student {
        // 使用学生的学号,作为索引id(_id字段)
        @Id
        private Long id;
        private String name;
        private Character gender;
    
        //@Field设置类中的属性对应索引中的字段名的关系,如果同名可以省略
        @Field("birthDate")          //索引中的字段名
        private String birthDate;    //对应类中的属性
    }
    
    • 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
    • @Document 注解
      • @Documnet注解对索引的参数进行设置。
      • 上面代码中,把 students 索引的分片数设置为3,副本数设置为2。
    • @Id 注解
      • 在 Elasticsearch 中创建文档时,使用 @Id 注解的字段作为文档的 _id 值
    • @Field 注解
      • 通过 @Field 注解设置字段的数据类型和其他属性。
    • 文本类型 text 和 keyword
      • text 类型会进行分词。
      • keyword 不会分词。
    • analyzer 指定分词器
      • 通过 analyzer 设置可以指定分词器,例如 ik_smart、ik_max_word 等。

    我们这个例子中,对学生姓名字段使用的分词器是 ngram 分词器,其分词效果如下面例子所示:

    字符串分词结果
    刘德华
    刘德
    刘德华
    德华

    5.通过 ElasticsearchRepository 实现 CRUD 操作

    Spring Data 的 Repository 接口提供了一种声明式的数据操作规范,无序编写任何代码,只需遵循 Spring Data 的方法定义规范即可完成数据的 CRUD 操作。

    ElasticsearchRepository 继承自 Repository,其中已经预定义了基本的 CURD 方法,我们可以通过继承 ElasticsearchRepository,添加自定义的数据操作方法。

    5.1 StudentRepository接口

    package cn.study.es;
    
    import cn.study.entity.Student;
    import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
    import java.util.List;
    
    
    /**
     * 我们这里基于Spring Data API 的Repository数据访问规范进行业务测试
     * 这种规范不需要我们自己完成相关代码,只需要定义抽象类,抽象方法就可以访问数据库的数据
     * 
     * 接口中已经定义了基础的增删改查方法
     * 继承的接口中需要传入俩个参数(访问学生数据,ID是Long类型)
     */
    public interface StudentRepository extends ElasticsearchRepository<Student,Long> {
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    5.2 业务类 StudentService

    package cn.tedu.esspringboot.es;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    @Service
    public class StudentService {
        @Autowired
        private StudentRepository studentRepo;
    
        public void save(Student student) {
            studentRepo.save(student);
        }
    
        public void delete(Long id) {
            studentRepo.deleteById(id);
        }
    
        public void update(Student student) {
            save(student);
        }
    
        public List<Student> findByName(String name) {
            return studentRepo.findByName(name);
        }
    
        public List<Student> findByNameOrBirthDate(String name, String birthDate) {
            return studentRepo.findByNameOrBirthDate(name, birthDate);
        }
    }
    
    • 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

    5.3 测试学生数据的 CRUD 操作

    添加测试类,对学生数据进行 CRUD 测试

    package cn.study;
    
    import cn.study.entity.Student;
    import cn.study.es.StudentRepository;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import java.util.Optional;
    
    @SpringBootTest
    public class Test1 {
    
        @Autowired
        private StudentRepository studentRepository;
    
        //调用API提供的save方法将数据存储到索引中
        @Test
        public void test1(){
            studentRepository.save(new Student(9527L,"卡布达",'男',"2023-10-11"));
            studentRepository.save(new Student(9528L,"蜻蜓队长",'男',"2023-10-12"));
            studentRepository.save(new Student(9529L,"鲨鱼辣椒",'男',"2023-10-13"));
            studentRepository.save(new Student(9530L,"蟑螂恶霸",'男',"2023-10-14"));
            studentRepository.save(new Student(9531L,"丸子滚滚",'男',"2023-10-15"));
            studentRepository.save(new Student(9532L,"蝎子莱莱",'男',"2023-10-16"));
            studentRepository.save(new Student(9533L,"金贵次郎",'男',"2023-10-17"));
            studentRepository.save(new Student(9534L,"呱呱蛙",'男',"2023-10-18"));
            studentRepository.save(new Student(9535L,"蜘蛛侦探",'男',"2023-10-19"));
            studentRepository.save(new Student(9536L,"呱呱蛙",'男',"2023-10-20"));
            studentRepository.save(new Student(9537L,"呱呱蛙",'男',"2023-10-21"));
            studentRepository.save(new Student(9538L,"呱呱蛙",'男',"2023-10-22"));
            studentRepository.save(new Student(9539L,"呱呱蛙",'男',"2023-10-23"));
        }
    
        //更新9527数据的名字和时间
        @Test
        public void test2(){
            studentRepository.save(new Student(9527L,"巨人卡布达",'男',"2023-10-19"));
        }
    
        /**
         * 查询操作:
         * java.util.Optinal 防止出现空指针异常
         */
        @Test
        public void test3(){
            Optional<Student> op = studentRepository.findById(9527L);
            if(op.isPresent()){//判断内部包含的对象是否存在
                Student s = op.get();
                System.out.println(s);
            }
    
            System.out.println("----------------------------------------------");
    
            Iterable<Student> all = studentRepository.findAll();
            for (Student s :
                    all) {
                System.out.println(s);
            }
        }
    
        //删除操作
        @Test
        public void test4(){
            studentRepository.deleteById(9530L);
        }
    }
    
    • 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

    依次运行每个测试方法,并使用 head 观察测试结果

    在这里插入图片描述

    并且通过打印的日志我们可以看到,http请求通过PUT请求添加数据,通过POST请求获取状态标志:
    在这里插入图片描述

    6.使用 Repository 构建查询

    Spring Data Elasticsearch 中,可以使用 SearchOperations 工具执行一些更复杂的查询,这些查询操作接收一个 Query 对象封装的查询操作。

    Spring Data Elasticsearch 中的 Query 有三种:

    • CriteriaQuery
    • StringQuery
    • NativeSearchQuery

    多数情况下,CriteriaQuery 都可以满足我们的查询求。下面来看两个 Criteria 查询示例:

    6.1 Repository 方法命名规范

    自定义数据操作方法需要遵循 Repository 规范,示例如下:

    关键词方法名es查询
    AndfindByNameAndPrice{ “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } }, { “query_string” : { “query” : “?”, “fields” : [ “price” ] } } ] } }}
    OrfindByNameOrPrice{ “query” : { “bool” : { “should” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } }, { “query_string” : { “query” : “?”, “fields” : [ “price” ] } } ] } }}
    IsfindByName{ “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } } ] } }}
    NotfindByNameNot{ “query” : { “bool” : { “must_not” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } } ] } }}
    BetweenfindByPriceBetween{ “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }}
    LessThanfindByPriceLessThan{ “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : false } } } ] } }}
    LessThanEqualfindByPriceLessThanEqual{ “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }}
    GreaterThanfindByPriceGreaterThan{ “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : false, “include_upper” : true } } } ] } }}
    GreaterThanEqualfindByPriceGreaterThan{ “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : true, “include_upper” : true } } } ] } }}
    BeforefindByPriceBefore{ “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }}
    AfterfindByPriceAfter{ “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : true, “include_upper” : true } } } ] } }}
    LikefindByNameLike{ “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?*”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }}
    StartingWithfindByNameStartingWith{ “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?*”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }}
    EndingWithfindByNameEndingWith{ “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “*?”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }}
    Contains/ContainingfindByNameContaining{ “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }}
    In (when annotated as FieldType.Keyword)findByNameIn(Collectionnames){ “query” : { “bool” : { “must” : [ {“bool” : {“must” : [ {“terms” : {“name” : [“?”,“?”]}} ] } } ] } }}
    InfindByNameIn(Collectionnames){ “query”: {“bool”: {“must”: [{“query_string”:{“query”: “”?" “?”", “fields”: [“name”]}}]}}}
    NotIn (when annotated as FieldType.Keyword)findByNameNotIn(Collectionnames){ “query” : { “bool” : { “must” : [ {“bool” : {“must_not” : [ {“terms” : {“name” : [“?”,“?”]}} ] } } ] } }}
    NotInfindByNameNotIn(Collectionnames){“query”: {“bool”: {“must”: [{“query_string”: {“query”: “NOT(”?" “?”)", “fields”: [“name”]}}]}}}
    NearfindByStoreNear
    TruefindByAvailableTrue{ “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “true”, “fields” : [ “available” ] } } ] } }}
    FalsefindByAvailableFalse{ “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “false”, “fields” : [ “available” ] } } ] } }}
    OrderByfindByAvailableTrueOrderByNameDesc{ “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “true”, “fields” : [ “available” ] } } ] } }, “sort”:[{“name”:{“order”:“desc”}}] }

    例如上表中的findByName方法,query后面的是当前方法的搜索关键词,fields字段后面跟着的就是查询的字段;

    在这里插入图片描述

    使用此类查询,一定遵循表格中的方法命名规范;

    6.2 Repository API查询案例

    我们在StudentRepository接口中构建Repository API提供的查询方法:

    package cn.study.es;
    
    import cn.study.entity.Student;
    import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
    import java.util.List;
    
    
    /**
     * 我们这里基于Spring Data API 的Repository数据访问规范进行业务测试
     * 这种规范不需要我们自己完成相关代码,只需要定义抽象类,抽象方法就可以访问数据库的数据
     *
     * 接口中已经定义了基础的增删改查方法
     * 继承的接口中需要传入俩个参数(访问学生数据,ID是Long类型)
     */
    public interface StudentRepository extends ElasticsearchRepository<Student,Long> {
    
        //使用name字段搜索关键词
        List<Student> findByName(String key);
        
        //使用name字段或者birthdate字段中搜索关键词
        List<Student> findByNameOrBirthDate(String name ,String birthdate);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    一定要注意方法名验证遵循6.1 Repository 方法命名规范中的要求;

    6.3 创建测试方法进行测试

    package cn.study;
    
    import cn.study.entity.Student;
    import cn.study.es.StudentRepository;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import java.util.List;
    
    /**
     * Repository 构建查询案例
     * */
    @SpringBootTest
    public class Test2 {
    
        @Autowired
        private StudentRepository studentRepository;
    
        @Test
        public void  test1(){
            //可以使用单独的关键字进行搜索
            List<Student> list1 = studentRepository.findByName("卡");
            for (Student  s1: list1) {
                System.out.println(s1);
            }
    
            System.out.println("------------------------");
    
            //但是对于日期是不分词的,无法通过分词进行搜索
            List<Student> list2 = studentRepository.findByNameOrBirthDate("呱", "2023-10-18");
            for (Student  s2: list2) {
                System.out.println(s2);
            }
        }
    
    }
    
    • 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

    7.分页操作

    分页对于搜索来说十分重要,大量数据的操作必须进行分页操作,否则十分影响性能及客户体验;Repository API 同样提供了分页的工具;

    7.1 Pageable

    Pageable 封装了向服务器提交的分页参数–页数及每页数据 ,第1页数据,提交数据0,第2页数据,提交数据1,以此类推;

    并且其提供了一个可选参数 page,封装服务器返回的这一页数据,以及所有的分页属性;

    package cn.tedu.esspringboot.es;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
    import org.springframework.data.elasticsearch.core.SearchHit;
    import org.springframework.data.elasticsearch.core.SearchHits;
    import org.springframework.data.elasticsearch.core.query.Criteria;
    import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    import java.util.stream.Collectors;
    
    @Component
    public class StudentSearcher {
        @Autowired
        private ElasticsearchOperations searchOperations;
    
        public List<Student> searchByBirthDate(String birthDate) {
            Criteria c = new Criteria("birthDate").is(birthDate);
            return criteriaSearch(c);
        }
    
        public List<Student> searchByBirthDate(String ge, String le) {
            Criteria c = new Criteria("birthDate").between(ge, le);
            return criteriaSearch(c);
        }
    
        private List<Student> criteriaSearch(Criteria c) {
            CriteriaQuery q = new CriteriaQuery(c);
            SearchHits<Student> hits = searchOperations.search(q, Student.class);
            List<Student> list = hits.stream().map(SearchHit::getContent).collect(Collectors.toList());
            return list;
        }
    }
    
    • 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

    7.2 修改 StudentService

    改造StudentService接口:

    package cn.study.es;
    
    import cn.study.entity.Student;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
    import java.util.List;
    
    
    /**
     * 我们这里基于Spring Data API 的Repository数据访问规范进行业务测试
     * 这种规范不需要我们自己完成相关代码,只需要定义抽象类,抽象方法就可以访问数据库的数据
     *
     * 接口中已经定义了基础的增删改查方法
     * 继承的接口中需要传入俩个参数(访问学生数据,ID是Long类型)
     */
    public interface StudentRepository extends ElasticsearchRepository<Student,Long> {
    
        //使用name字段搜索关键词
        List<Student> findByName(String key);
    
        //使用name字段或者birthdate字段中搜索关键词
        //List findByNameOrBirthDate(String name ,String birthdate);
    
        //改造findByNameOrBirthDate方法
        //List findByNameOrBirthDate(String name , String birthdate, Pageable pageable);
    
        //也可以改造为使用Page可选参数的方法
        Page<Student> findByNameOrBirthDate(String name , String birthdate, Pageable pageable);
    }
    
    
    • 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

    7.3 在测试类中添加测试方法

    package cn.study;
    
    import cn.study.entity.Student;
    import cn.study.es.StudentRepository;
    import org.junit.jupiter.api.Test;
    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 java.util.List;
    
    /**
     * Repository 构建查询案例
     * */
    @SpringBootTest
    public class Test2 {
    
        @Autowired
        private StudentRepository studentRepository;
    
        @Test
        public void  test1(){
            //可以使用单独的关键字进行搜索
            List<Student> list1 = studentRepository.findByName("卡");
            for (Student  s1: list1) {
                System.out.println(s1);
            }
    
            System.out.println("------------------------");
    
            //但是对于日期是不分词的,无法通过分词进行搜索
    //        List list2 = studentRepository.findByNameOrBirthDate("呱", "2023-10-18");
    //        for (Student  s2: list2) {
    //            System.out.println(s2);
    //        }
    
    
            //改造findByNameOrBirthDate方法
            //使用pageable的实现类PageRequest创建pageable对象,第1页,每页3条数据;
            Pageable pageable = PageRequest.of(0,3);
            Page<Student> list2 = studentRepository.findByNameOrBirthDate("呱", "2023-10-18", pageable );
            for (Student  s2: list2) {
                System.out.println(s2);
            }
        }
    
    }
    
    • 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

    8.查询结果的高亮显示

    8.1 接口方法

    我们在接口中创建新的方法findByNameNot来测试查询结果的高亮显示:

        //搜索不包含输入字段的关键词
        @Highlight(
                //设置parameters参数,通过@HighlightParameters设置前置preTags标签和postTags后置标签
                parameters = @HighlightParameters(
                        preTags = "",
                        postTags = ""),
                //设置fields高亮字段
                fields = {@HighlightField(name = "name")}
        )
        List<SearchHit<Student>> findByNameNot(String key);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    查询结果的高亮显示通过 @Highlight注解进行设置,其包含俩个参数:

    • parameters:需要通过@HighlightParameters注解作为值传入设置parameters高亮参数,@HighlightParameters注解还需要设置标签;
      • ·preTags·参数:设置前置标签;
      • postTags参数:设置后置标签
    • fields:通过@HighlightField注解作为值传入设置fields参数设置具体哪些字段高亮,@HighlightField注解还需要给定name参数的值
      • name参数:具体哪些字段高亮,@HighlightField注解中的参数为一个数组,通过数组的形式可以给定多个具体字段;

    需要注意的是,此前我们创建的findByNameOrBirthDate不能使用高亮注解,使用高亮注解后,发送消息会同时将高亮注解中设置的相关参数也一起返回给客户端,Page类型与List类型均不可包含此些数据;

    我们需要使用springboot封装的SearchHit类型将其与List,Student组成嵌套关系的返回类型,获取高亮结果;SearchHit中封装了高亮类型 的结果,我们Student对象中只包含原始学生数据,所以需要SearchHit来封装高亮字段的相关内容;
    在这里插入图片描述

    此时SearchHit里会包含高亮属性:name = xxx

    8.2 测试类方法

        //查询结果高亮显示
        @Test
        public void test2(){
            List<SearchHit<Student>> list1 = studentRepository.findByNameNot("不包含此字段");
            List<Student> stuList = new ArrayList<>();
            for (SearchHit<Student>  sh: list1) {
                //取出学生对象
                Student stu = sh.getContent();
                //取出高亮对象中的name值
                List<String> name = sh.getHighlightField("name");
                //将高亮对象中的name值拼接好
                String hiName = pinjie(name);
                //将高亮name值放到学生对象中
                stu.setName(hiName);
                stuList.add(stu);
            }
    
            for (Student s : stuList) {
                System.out.println(s);
            }
        }
    
        private String pinjie(List<String> name){
            //name=["xxx",","xxx",""]
            StringBuilder sbu = new StringBuilder();
            for (String sn : name) {
                sbu.append(sn);
            }
            return sbu.toString();
        }
    
    • 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

    9.使用 Criteria 构建查询

    Spring Data Elasticsearch 中,除了ElasticsearchRepository外,还提供了CriteriaAPI ;可以使用 SearchOperations 工具执行一些更复杂的查询,这些查询操作接收一个 Query 对象封装的查询条件,然后使用CriteriaQuery封装Criteria对象分页参数等,最后使用ElasticsearchOperations执行Query 对象

    区别于ElasticsearchRepository严格的方法命名规范,Criteria对方法命名没有任何要求;

    Spring Data Elasticsearch 中的 Query 有三种:

    • CriteriaQuery
    • StringQuery
    • NativeSearchQuery

    多数情况下,CriteriaQuery 都可以满足我们的查询求。下面来看两个 Criteria 查询示例:

    9.1 StudentSearcher

    package cn.study.es;
    
    import cn.study.entity.Student;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
    import org.springframework.data.elasticsearch.core.SearchHit;
    import org.springframework.data.elasticsearch.core.SearchHits;
    import org.springframework.data.elasticsearch.core.query.Criteria;
    import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Component
    public class StudentSearche {
        //用来执行查询的工具对象
        @Autowired
        private ElasticsearchOperations elasticsearchOperations;
    
        //按照姓名搜索
        public List<Student> findByName(String name){
            //创建Criteria对象
            Criteria c = new Criteria("name");
            //在name字段搜索指定的关键词
            c.is(name);
            return exec(c,null);
        }
    
        //按照日期范围搜索,并添加分页
        public List<Student> findByBirthDate(String from, String to, Pageable pageable){
            //创建Criteria对象
            Criteria c = new Criteria("birthDate");
            //在name字段搜索指定的关键词
            c.between(from,to);
            return exec(c,pageable);
        }
    
        private List<Student> exec(Criteria c, Pageable pageable) {
            //使用CriteriaQuery封装Criteria对象
            CriteriaQuery q = new CriteriaQuery(c);
            if(pageable != null){
                q.setPageable(pageable);
            }
            //使用elasticsearchOperations对象进行搜索,并把搜索结果封装成student对象(默认返回是一个json串)
            SearchHits<Student> search = elasticsearchOperations.search(q, Student.class);
            List<Student> list = new ArrayList<>();
            for (SearchHit<Student> sh : search) {
                list.add(sh.getContent());
            }
            return list;
        }
    }
    
    • 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

    9.2 在测试类中测试

    package cn.study;
    
    import cn.study.entity.Student;
    import cn.study.es.StudentSearche;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.domain.PageRequest;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @SpringBootTest
    public class Test3 {
    
        @Autowired
        private StudentSearche studentSearche;
    
        @Test
        public void test1(){
            List<Student> list = studentSearche.findByName("呱");
            System.out.println(list);
        }
    
        @Test
        public void test2(){
            PageRequest p = PageRequest.of(0, 2);
            List<Student> list = studentSearche.findByBirthDate("2023-10-15","2023-10-19",p);
            System.out.println(list);
        }
    }
    
    • 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
  • 相关阅读:
    8月更新| Java on Visual Studio Code
    HTML使用canvas绘制海报(网络图片)
    解析分布式系统的缓存设计
    高通平台开发系列讲解(USB篇)Linux Android USB软件架构
    kafka 开启认证授权
    Cache系列直播,这次真的来了!
    C++_访问(权限)控制和继承
    net-java-php-python-网络安全教育学习网站计算机毕业设计程序
    git常用操作记录
    微服务学习第三十三节
  • 原文地址:https://blog.csdn.net/whatfuswd/article/details/133782089