Elasticsearch(五)Spring Data Elasticsearch

Spring Data Elasticsearch

https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference

Spring Data Elasticsearch 是 Elasticsearch 搜索引擎开发的解决方案。它提供:

模板对象,用于存储、搜索、排序文档和构建聚合的高级API。

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

案例说明

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

数据结构:

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

案例测试以下数据操作:

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

创建项目

  1. 新建工程
    a
    a

  2. 新建 springboot module,添加 spring data elasticsearch 依赖
    在这里插入图片描述
    a
    a
    a

项目的 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 https://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.tedu</groupId>
	<artifactId>es-springboot</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>es-springboot</name>
	<description>Demo project for Spring Boot</description>

	<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>
	</dependencies>

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

</project>

application.yml 配置

logging.level.tracer=TRACE 作用是在控制台中显示底层的查询日志

spring:
  elasticsearch:
    rest:
      uris: http://192.168.64.181:9200

logging:
  level:
    tracer: TRACE

Student 实体类

package cn.tedu.esspringboot.es;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Document(indexName = "students",shards = 3,replicas = 2)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    @Id
    private Long id;

    @Field(analyzer = "ngram",type = FieldType.Text)
    private String name;

    @Field(type = FieldType.Keyword)
    private Character gender;

    @Field(type= FieldType.Date,format = DateFormat.custom,pattern = "yyyy-M-d")
    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 分词器,其分词效果如下面例子所示:

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

通过 ElasticsearchRepository 实现 CRUD 操作

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

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

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”]}}]}}}
NearfindByStoreNearNot Supported Yet !
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”}}] }

StudentRepository

package cn.tedu.esspringboot.es;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;

public interface StudentRepository extends ElasticsearchRepository<Student, Long> {
    List<Student> findByName(String name);

    List<Student> findByNameOrBirthDate(String name, String birthDate);
}

业务类 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);
    }
}

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

测试学生数据的 CRUD 操作

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

package cn.tedu.esspringboot;

import cn.tedu.esspringboot.es.Student;
import cn.tedu.esspringboot.es.StudentService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class Test1 {
    @Autowired
    private StudentService studentService;

    @Test
    public void test1() {
        studentService.save(new Student(998L,"张三",'男',"2020-12-04"));
    }
    @Test
    public void test2() {
        studentService.update(new Student(1L,"李四",'女',"2020-12-04"));
    }
    @Test
    public void test3() {
        List<Student> stu = studentService.findByName("四");
        System.out.println(stu);
    }
    @Test
    public void test4() throws Exception {
        List<Student> stu;

        stu = studentService.findByNameOrBirthDate("四", "1999-09-09");
        System.out.println(stu);

        stu = studentService.findByNameOrBirthDate("SFSDFS", "2020-12-04");
        System.out.println(stu);
    }
}

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

a

使用 Criteria 构建查询

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

Spring Data Elasticsearch 中的 Query 有三种:

  • CriteriaQuery
  • StringQuery
  • NativeSearchQuery

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

StudentSearcher

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

修改 StudentService

在 StudentService 中,调用 StudentSearcher,执行查询:

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;

    @Autowired
    private StudentSearcher studentSearcher;

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

    public List<Student> findByBirthDate(String birthDate) {
        return studentSearcher.searchByBirthDate(birthDate);
    }

    public List<Student> findByBirthDate(String ge, String le) {
        return studentSearcher.searchByBirthDate(ge, le);
    }
}

在测试类中添加测试方法

package cn.tedu.esspringboot;

import cn.tedu.esspringboot.es.Student;
import cn.tedu.esspringboot.es.StudentService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class Test1 {
    @Autowired
    private StudentService studentService;

    @Test
    public void test1() {
        studentService.save(new Student(998L,"张三",'男',"2020-12-04"));
    }
    @Test
    public void test2() {
        studentService.update(new Student(1L,"李四",'女',"2020-12-04"));
    }
    @Test
    public void test3() {
        List<Student> stu = studentService.findByName("四");
        System.out.println(stu);
    }
    @Test
    public void test4() throws Exception {
        List<Student> stu;

        stu = studentService.findByNameOrBirthDate("四", "1999-09-09");
        System.out.println(stu);

        stu = studentService.findByNameOrBirthDate("SFSDFS", "2020-12-04");
        System.out.println(stu);
    }

    @Test
    public void test5() throws Exception {
        List<Student> stu;

        stu = studentService.findByBirthDate("2020-12-04");
        System.out.println(stu);
    }

    @Test
    public void test6() throws Exception {
        List<Student> stu;

        stu = studentService.findByBirthDate("2020-12-05", "2020-12-09");
        System.out.println(stu);
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青木编码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值