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

news2025/1/16 8:22:49

文章目录

  • 1.Spring Data `Elasticsearch`
  • 2.案例准备
    • 2.1 在 Elasticsearch 中创建 students 索引
    • 2.2 案例测试说明
  • 3.创建项目
    • 3.1 新建工程
    • 3.2 新建 springboot module,添加 spring data elasticsearch 依赖
    • 3.3 pom.xml 文件
    • 3.4 application.yml 配置
  • 4.Student 实体类
  • 5.通过 ElasticsearchRepository 实现 CRUD 操作
    • 5.1 StudentRepository接口
    • 5.2 业务类 StudentService
    • 5.3 测试学生数据的 CRUD 操作
  • 6.使用 Repository 构建查询
    • 6.1 Repository 方法命名规范
    • 6.2 Repository API查询案例
    • 6.3 创建测试方法进行测试
  • 7.分页操作
    • 7.1 Pageable
    • 7.2 修改 StudentService
    • 7.3 在测试类中添加测试方法
  • 8.查询结果的高亮显示
    • 8.1 接口方法
    • 8.2 测试类方法
  • 9.使用 Criteria 构建查询
    • 9.1 StudentSearcher
    • 9.2 在测试类中测试


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"
      }
    }
  }
}

创建索引之后,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 文件

<?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">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>cn.study</groupId>
    <artifactId>spring-data-es</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.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-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

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

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;    //对应类中的属性
}
  • @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> {

}

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);
    }
}

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);
    }
}

依次运行每个测试方法,并使用 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);

}

一定要注意方法名验证遵循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);
        }
    }

}

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;
    }
}

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<Student> findByNameOrBirthDate(String name ,String birthdate);

    //改造findByNameOrBirthDate方法
    //List<Student> findByNameOrBirthDate(String name , String birthdate, Pageable pageable);

    //也可以改造为使用Page可选参数的方法
    Page<Student> findByNameOrBirthDate(String name , String birthdate, Pageable pageable);
}

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<Student> 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);
        }
    }

}

8.查询结果的高亮显示

8.1 接口方法

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

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

查询结果的高亮显示通过 @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<em>卡</em>

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","<em>卡</em>,"xxx","<em>卡</em>"]
        StringBuilder sbu = new StringBuilder();
        for (String sn : name) {
            sbu.append(sn);
        }
        return sbu.toString();
    }

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;
    }
}

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);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1108083.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SD卡与emmc的异同

eMMC与SD卡的异同&#xff1a; 物理尺寸和接口&#xff1a; eMMC&#xff1a;eMMC是一种嵌入式存储解决方案&#xff0c;通常采用BGA&#xff08;Ball Grid Array&#xff09;封装&#xff0c;焊接在电路板上。它没有标准的物理尺寸&#xff0c;而是以芯片的形式存在。SD卡&…

代理模式(初学)

代理模式 一、什么是代理模式 代理模式&#xff1a;为其他对象提供一种代理以控制对这个对象的访问 二、简单例子 这里面的骏骏就起到了代理的身份&#xff0c;而贵贵则是被代理的身份。 三、代码实现 1、用一个接口&#xff08;GivingGifts&#xff09;来保存送礼物的动作…

链表的概念+MySingleList的实现

文章目录 链表一、 链表的概念1.概念2. 结构 二、MySingleList的实现1 .定义内部类2 .创建链表3. 遍历链表并打印4.查找单链表中是否包含关键字key5.得到链表的长度6.头插法7. 尾插法8.任意位置插入8.删除结点清空 链表 顺序存储&#xff1a;顺序表/ArrayList 优点&#xff1…

Android平台GB28181设备接入模块之SmartGBD

大牛直播SDK研发的Android平台GB28181设备接入SDK&#xff08;SmartGBD&#xff09;&#xff0c;可实现不具备国标音视频能力的 Android终端&#xff0c;通过平台注册接入到现有的GB/T28181—2016服务&#xff0c;可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育…

C#插入排序算法

插入排序实现原理 插入排序算法是一种简单、直观的排序算法&#xff0c;其原理是将一个待排序的元素逐个地插入到已经排好序的部分中。 具体实现步骤如下 首先咱们假设数组长度为n&#xff0c;从第二个元素开始&#xff0c;将当前元素存储在临时变量temp中。 从当前元素的前一…

【量化交易笔记】9.量化投资理论及一般流程

前言 在第7篇文章中指出&#xff0c;量化交易的主要有两方面应用&#xff0c;基于的数据主要是两个类型&#xff0c;如前面讲的用之前的数据预测股价&#xff0c;这类数据我们可归为纵向研究数据&#xff0c;又称时间序列数据&#xff0c;另一类是横截面数据&#xff0c;以称截…

CleanMyMac X2024免费版苹果电脑杀毒工具

在过去&#xff0c;人们普遍认为苹果电脑不容易受到恶意软件的攻击&#xff0c;因此不需要安装杀毒软件。然而&#xff0c;随着苹果电脑的普及和互联网的发展&#xff0c;苹果电脑也逐渐成为黑客和恶意软件的目标。为了保护苹果电脑的安全&#xff0c;使用一款可靠的苹果电脑杀…

通过SPI传输BMI160数据到nrf528xx

目录 主控和外设之间的联系关键示例可能的bug 主控和外设之间的联系 在完成代码之前&#xff0c;我们手里会有两份代码&#xff0c;一份是nrf528xx的SDK&#xff0c;一份是BMI160传感器的SDK&#xff0c;怎么利用SDK完成我们的需求呢&#xff1f;首先我们要搞明白&#xff0c;…

了解工业交换机背板带宽及其重要性

工业交换机是工业自动化领域中的关键设备&#xff0c;用于管理和控制工业网络中的数据流量。在设计和维护工业网络时&#xff0c;了解和优化工业交换机的性能至关重要。其中&#xff0c;背板带宽是一个重要的性能参数。 什么是工业交换机的背板带宽&#xff1f; 工业交换机的背…

Ubuntu系统如何进行网络连接-连接电脑局域网-物联网开发-Ubuntu系统维护

一、前言 在Ubuntu系统的维护中&#xff0c;我们常常需要对VMware中的Ubuntu虚拟机配置网络连接&#xff0c;以连接服务器下载或安装软件包以及进行网络通信等。 基于上述问题&#xff0c;本文将着重分享Ubuntu配置网络链接的若干方法。 二、网络连接模式 打开VM&#xff0c;右…

互联网Java工程师面试题·Java 总结篇·第八弹

目录 72、用 Java 的套接字编程实现一个多线程的回显&#xff08;echo&#xff09;服务器。 73、XML 文档定义有几种形式&#xff1f;它们之间有何本质区别&#xff1f;解析XML 文档有哪几种方式&#xff1f; 74、你在项目中哪些地方用到了 XML&#xff1f; 72、用 Java 的套…

初识RabbitMQ

大家好我是苏麟今天带来rabbitmq. RabbitMQ RabbitMQ官网 : RabbitMQ: easy to use, flexible messaging and streaming — RabbitMQ 初识MQ 同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步…

Illustrator 2022 for mac (AI 2022中文版)

Illustrator 软件是一种应用于出版、多媒体和在线图像的工业标准矢量插画的软件。作为一款非常好的矢量图形处理工具&#xff0c;该软件主要应用于印刷出版、海报书籍排版、专业插画、多媒体图像处理和互联网页面的制作等&#xff0c;也可以为线稿提供较高的精度和控制&#xf…

CCF CSP认证 历年题目自练Day35

题目一 试题编号&#xff1a; 202305-1 试题名称&#xff1a; 重复局面 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题描述&#xff1a; 题目背景 国际象棋在对局时&#xff0c;同一局面连续或间断出现3次或3次以上&#xff0c;可由任意一方提出和棋。 问题…

数学建模——最优连接(基于最小支撑树)

一、概念 1、图的生成树 由图G(V,E)的生成子图G1(V,E1)(E1是E的子集&#xff09;是一棵树&#xff0c;则称该树为图G的生成树&#xff08;支撑树&#xff09;&#xff0c;简称G的树。图G有支撑树的充分必要条件为图G连通。 2、最小生成树问题 连通图G(V,E)&#xff0c;每条边…

Linux系统管理:虚拟机OpenEuler安装

目录 一、理论 1.OpenEuler 二、实验 1.虚拟机OpenEuler安装准备阶段 2.安装OpenEuler 3.进入系统 一、理论 1.OpenEuler &#xff08;1&#xff09;简介 欧拉&#xff08;Euler&#xff09;是数字基础设施的开源操作系统&#xff0c;可广泛部署于服务器、云计算、边缘…

一款简单漂亮的WPF UI - AduSkin

前言 经常会有同学会问&#xff0c;有没有好看简单的WPF UI库推荐的。今天就给大家推荐一款简单漂亮的WPF UI&#xff0c;融合多个开源框架组件&#xff1a;AduSkin。 WPF是什么&#xff1f; WPF 是一个强大的桌面应用程序框架&#xff0c;用于构建具有丰富用户界面的 Windo…

[Machine Learning][Part 6]Cost Function代价函数和梯度正则化

目录 拟合 欠拟合 过拟合 正确的拟合 解决过拟合的方法&#xff1a;正则化 线性回归模型和逻辑回归模型都存在欠拟合和过拟合的情况。 拟合 来自百度的解释&#xff1a; 数据拟合又称曲线拟合&#xff0c;俗称拉曲线&#xff0c;是一种把现有数据透过数学方法来代入一条…

MySQL-逻辑架构

MySQL-逻辑架构 1.逻辑架构剖析 1.1服务器处理客户端请求 MySQL属于典型的C/S架构&#xff0c;客户端进程发送请求&#xff0c;服务端进程处理请求。处理的基本流程如下。 MySQL的逻辑架构如下&#xff1a; Connectors&#xff1a;与服务端程序连接的程序。 Manager Servic…

Unity之ShaderGraph如何实现卡通效果

前言 今天我们来实现一下最常见的卡通效果。 效果如下&#xff1a; 关键节点 Remap&#xff1a;基于输入 In 值在输入In Min Max的 x 和 y 分量之间的线性插值&#xff0c;返回输入Out Min Max的 x 和 y 分量之间的值。 SampleGradient&#xff1a;在给定Time输入的情况下…