Spring Data Elasticsearch基于Spring Data API简化 Elasticsearch 操作,将原始操作Elasticsearch 的客户端API进行封装。Spring Data为Elasticsearch 项目提供集成搜索引擎。Spring Data Elasticsearch POJO的关键功能区域为中心的模型与Elastichsearch交互文档和轻松地编写一个存储索引库数据访问层。
spring-data-elasticsearch与ES、SpringBoot的对应关系

Spring Data通过注解来声明字段的映射属性,有下面的三个注解:
@Document 作用在类,标记实体类为文档对象,一般有两个属性@Id 作用在成员变量,标记一个字段作为id主键@Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:本文使用的是:
spring-boot(2.3.12.RELEASE)
spring-data-elasticsearch(2.3.12.RELEASE)
spring-data-elasticsearch
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
全部pom
<?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.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>ES-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ES-test</name>
<description>ES-test</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<!-- junit 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring.elasticsearch.rest.uris=http://127.0.0.1:9200
spring.elasticsearch.rest.read-timeout= 30
spring.elasticsearch.rest.connection-timeout= 30
请求地址:http://localhost:9200/blog
请求类型:PUT
请求体:
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"id":{
"type":"long"
},
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"author":{
"type": "text"
},
"category":{
"type": "keyword"
},
"createTime": {
"type": "date",
"format":"yyyy-MM-dd HH:mm:ss.SSS||yyyy-MM-dd'T'HH:mm:ss.SSS||yyyy-MM-dd HH:mm:ss||epoch_millis"
},
"updateTime": {
"type": "date",
"format":"yyyy-MM-dd HH:mm:ss.SSS||yyyy-MM-dd'T'HH:mm:ss.SSS||yyyy-MM-dd HH:mm:ss||epoch_millis"
},
"status":{
"type":"integer"
},
"serialNum": {
"type": "keyword"
}
}
}
}
import com.fasterxml.jackson.annotation.JsonFormat;
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;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "blog", shards = 1, replicas = 1)
public class Blog {
//此项作为id,不会写到_source里边。
@Id
private Long blogId;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String content;
@Field(type = FieldType.Text)
private String author;
//博客所属分类。
@Field(type = FieldType.Keyword)
private String category;
//0: 未发布(草稿) 1:已发布 2:已删除
@Field(type = FieldType.Integer)
private int status;
//序列号,用于给外部展示的id
@Field(type = FieldType.Keyword)
private String serialNum;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
@Field(type= FieldType.Date, format= DateFormat.custom, pattern="yyyy-MM-dd HH:mm:ss.SSS")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
@Field(type=FieldType.Date, format=DateFormat.custom, pattern="yyyy-MM-dd HH:mm:ss.SSS")
private Date updateTime;
}
import com.example.estest.bean.Blog;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* 自定义的dao.类似于mybatis的dao,在此处可以自定义方法
*/
public interface BlogRepository extends ElasticsearchRepository<Blog, Long> {
}
import com.example.estest.bean.Blog;
import com.example.estest.dao.BlogRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* 测试ES
*/
@RestController
@RequestMapping("crud")
public class CrudController {
@Autowired
private BlogRepository blogRepository;
/**
* 添加单个文档
* @return
*/
@GetMapping("addDocument")
public Blog addDocument() {
Long id = 1L;
Blog blog = new Blog();
blog.setBlogId(id);
blog.setTitle("我是一个大标题" + id);
blog.setContent("这是添加单个文档的实例" + id);
blog.setAuthor("wf");
blog.setCategory("ElasticSearch");
blog.setCreateTime(new Date());
blog.setStatus(1);
blog.setSerialNum(id.toString());
blog.setUpdateTime(new Date());
return blogRepository.save(blog);
}
/**
* 查找指定文档
* @param id
* @return
*/
@GetMapping("findById")
public Blog findById(Long id) {
return blogRepository.findById(id).get();
}
/**
* 添加多个文档
* @param count
* @return
*/
@GetMapping("addDocuments")
public Object addDocuments(Integer count) {
List<Blog> blogs = new ArrayList<>();
for (int i = 1; i <= count; i++) {
Long id = (long)i;
Blog blog = new Blog();
blog.setBlogId(id);
blog.setTitle("我是一个大标题" + id);
blog.setContent("这是博客内容" + id);
blog.setAuthor("wf");
blog.setCategory("ElasticSearch");
blog.setCreateTime(new Date());
blog.setStatus(1);
blog.setSerialNum(id.toString());
blogs.add(blog);
}
return blogRepository.saveAll(blogs);
}
/**
* 修改单个文档,相当于是覆盖,没有设置的内容为null,也会一同替换
* @return
*/
@GetMapping("editDocument")
public Blog editDocument(Long id) {
Blog blog = new Blog();
blog.setBlogId(id);
blog.setTitle("Spring Data ElasticSearch学习教程" + id);
blog.setContent("这是修改单个文档的实例" + id);
return blogRepository.save(blog);
}
/**
* 删除指定文档
* @param id
* @return
*/
@GetMapping("deleteDocument")
public String deleteDocument(Long id) {
blogRepository.deleteById(id);
return "success";
}
/**
* 删除所有文档
* @return
*/
@GetMapping("deleteDocumentAll")
public String deleteDocumentAll() {
blogRepository.deleteAll();
return "success";
}
/**
* 查询全部文档
* @return
*/
@GetMapping("queryAll")
public Object queryAll() {
return blogRepository.findAll();
}
/**
* 分页查询文档
*
* 注意:
* 这里的分页和mysql的分页有所不同,此处分页只需要传递页码即可,无需计算起始位置
* 第一页的页码为 0, 第二页为 1,第三页为 2,以此类推
*
*/
@GetMapping("findByPageable/{page}/{size}")
public List<Blog> findByPageable(@PathVariable("page") Integer page, @PathVariable("size") Integer size){
//设置排序(排序方式,正序还是倒序,排序的 id)
Sort sort = Sort.by(Sort.Direction.DESC,"blogId");
//设置查询分页
PageRequest pageRequest = PageRequest.of(page, size,sort);
//分页查询
Page<Blog> productPage = blogRepository.findAll(pageRequest);
System.out.println("总页数:"+productPage.getTotalPages());
System.out.println("分页参数信息:"+productPage.getPageable());
System.out.println("总条数:"+productPage.getTotalElements());
System.out.println("当前页码:"+productPage.getNumber());
System.out.println("单页展示条数:"+productPage.getNumberOfElements());
System.out.println("排序字段及方式:"+productPage.getSort());
System.out.println("当前集合总数:"+productPage.getSize());
return productPage.getContent();
}
}
跟Spring Data JPA类似,spring data elsaticsearch提供了自定义方法的查询方式,
在Repository接口中自定义方法,spring data根据方法名,自动生成实现类,但是方法名必须符合一定的规则,
在自定义的Dao中编写如下方法:
import com.example.estest.bean.Blog;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.HighlightParameters;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
/**
* 自定义的dao.类似于mybatis的dao,在此处可以自定义方法
*/
public interface BlogRepository extends ElasticsearchRepository<Blog, Long> {
/**
* 自定义根据title搜索查询,并高亮显示title属性
* 此处高亮使用的是,默认的 <em></em> 斜体标签
*
* @param title 模糊查询的 title 值
* @return
*/
@Highlight(fields = {
@HighlightField(name = "title")
})
List<SearchHit<Blog>> findByTitle(String title);
/**
* 根据content模糊查询
* 此处高亮使用的是,自定义的span标签
*
* @param content 模糊查询的 title 值
* @return
*/
@Highlight(fields = {
@HighlightField(name = "content")
}, parameters = @HighlightParameters(
preTags = "<span style='color:red'>",
postTags = "</span>"
))
List<SearchHit<Blog>> findByContent(String content);
/**
* 根据title,content模糊查询
* 此处高亮使用的是,自定义的span标签
*
* @param content 模糊查询的 title 值
* @return
*/
@Highlight(
fields = {
@HighlightField(name = "title"),
@HighlightField(name = "content")
},
parameters = @HighlightParameters(
preTags = "<span style='color:red'>",
postTags = "</span>"
))
List<SearchHit<Blog>> findByTitleAndContent(String title, String content);
}
编写接口:
import com.example.estest.bean.Blog;
import com.example.estest.dao.BlogRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* 测试ES
*/
@RestController
@RequestMapping("crud")
public class CrudController {
@Autowired
private BlogRepository blogRepository;
……
此处省略上文接口
……
/**
* 默认高亮标签模糊查询 title
* @param title
* @return
*/
@GetMapping("highlightQueryTitle/{title}")
public List<SearchHit<Blog>> highlightQueryTitle(@PathVariable("title") String title) {
return blogRepository.findByTitle(title);
}
/**
* 自定义高亮标签查询 content
* @param content
* @return
*/
@GetMapping("highlightQueryContent/{content}")
public List<SearchHit<Blog>> highlightQueryContent(@PathVariable("content") String content) {
return blogRepository.findByContent(content);
}
/**
* 自定义高亮标签查询 content 和 title
* @param content
* @return
*/
@GetMapping("highlightQueryTitleAndContent/{title}/{content}")
public List<SearchHit<Blog>> highlightQueryTitleAndContent(@PathVariable("title") String title,@PathVariable("content") String content) {
return blogRepository.findByTitleAndContent(title,content);
}
}

跟JPA一样,Spring Data ElasticSearch可以使用@Query自定义语句进行查询。
但Spring Data ElasticSearch不能通过冒号指定参数(比如:title),只能用问号加序号,比如?0)
在自定义的Dao中编写如下方法:
import com.example.estest.bean.Blog;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.HighlightParameters;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
/**
* 自定义的dao.类似于mybatis的dao,在此处可以自定义方法
*/
public interface BlogRepository extends ElasticsearchRepository<Blog, Long> {
………………
………此处省略前文内容………
………………
/**
* 根据标题和内筒查询
* @param title
* @param content
* @return
*/
@Query("{\"bool\":{\"must\":[{\"match\":{\"title\":\"?0\"}}," +
"{\"match\":{\"content\":\"?1\"}}]}}")
List<Blog> findByTitleAndContentCustom(@Param("title") String title, @Param("content") String content);
/**
* 根据标题和内容分页查询
* @param title
* @param content
* @param pageable
* @return
*/
@Query("{\"bool\":{\"must\":[{\"match\":{\"title\":\"?0\"}}," +
"{\"match\":{\"content\":\"?1\"}}]}}")
Page<Blog> findByTitleAndContentCustom(@Param("title") String title, @Param("content") String content,Pageable pageable);
}
编写接口:
import com.example.estest.bean.Blog;
import com.example.estest.dao.BlogRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* 测试ES
*/
@RestController
@RequestMapping("crud")
public class CrudController {
@Autowired
private BlogRepository blogRepository;
………………
………此处省略前文内容………
………………
/**
* 根据标题和内筒查询
* @param title
* @param content
* @return
*/
@GetMapping("listByTitleAndContent/{title}/{content}")
public List<Blog> listByTitleAndContent(@PathVariable("title") String title,@PathVariable("content") String content) {
return blogRepository.findByTitleAndContentCustom(title, content);
}
/**
* 根据标题和内容分页查询
* @param title
* @param content
* @return
*/
@GetMapping("pageByTitleAndContent/{title}/{content}")
public Page<Blog> pageByTitleAndContent(@PathVariable("title") String title,@PathVariable("content") String content) {
PageRequest pageRequest = PageRequest.of(0, 2);
return blogRepository.findByTitleAndContentCustom(title, content, pageRequest);
}
}
