BoolQueryBuilder 和 wildcardQuery 查询
在上面几篇elasticsearch文章的基础上,本篇将SpringBoot 集成 elasticsearch 。
在集成前,先放一张图,这个要严格遵守,否则会出现各种各样的问题。我本地使用的是红框标出来的这个版本。
ES的使用常见分为2种,一是用ElasticsearchRepository,一种是用ElasticsearchTemplate模版(下面的Controller层代码,有这两种的展示)。
JPA中有个ElasticsearchRepository可以做Elasticsearch的相关增删改查,用法和普通的CRUDRepository是一样的,这样就能统一ElasticSearch和普通的JPA操作,获得和操作mysql一样的代码体验。但是同时可以看到ElasticsearchRepository的功能是比较少的,简单查询够用,但复杂查询就不够了。
而ElasticsearchTemplate则提供了更多的方法来完成更多的功能,也包括分页之类的,他其实就是一个封装好的ElasticSearch Util功能类,通过直接连接client来完成数据的操作,推荐使用ElasticsearchTemplate。
一. Springboot集成ES
我这里springboot版本2.1.7,本地ES服务版本6.2.2,IK版本6.3.0
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.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.robinson</groupId>
<artifactId>elasticsearchDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elasticsearchDemo</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-web</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties文件配置
#===== elasticsearch 配置 ====
##开启 Elasticsearch 仓库(默认值:true)
#spring.data.elasticsearch.repositories.enabled=true
##默认 9300 是 Java 客户端的端口。9200 是支持 Restful HTTP 的接口
spring.data.elasticsearch.cluster-nodes=localhost:9300
##spring.data.elasticsearch.cluster-name Elasticsearch 集群名(默认值: elasticsearch)
spring.data.elasticsearch.cluster-name=elasticsearch
##spring.data.elasticsearch.cluster-nodes 集群节点地址列表,用逗号分隔。如果没有指定,就启动一个客户端节点
##spring.data.elasticsearch.propertie 用来配置客户端的额外属性
##存储索引的位置
#spring.data.elasticsearch.properties.path.home=/data/project/target/elastic
##连接超时的时间
#spring.data.elasticsearch.properties.transport.tcp.connect_timeout=120s
BlogModel类
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Date;
@Document(indexName = "blog", type = "java")
public class BlogModel implements Serializable {
private static final long serialVersionUID = 6320548148250372657L;
@Id
private String id;
@Size(min = 3, max = 10)
private String title;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;
public BlogModel() {
super();
}
public BlogModel(String id, String title, Date date) {
super();
this.id = id;
this.title = title;
this.date = date;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
定义BlogRepository接口
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface BlogRepository extends ElasticsearchRepository<BlogModel, String> {
Page<BlogModel> findByTitle(String title, Pageable pageable);
@Query("{\"match_phrase\":{\"title\":\"?0\"}}")
List<BlogModel> findByTitleCustom(String keyword);
}
直接看Controller层的demo代码,如下:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
public class BlogController {
@Autowired
private BlogRepository blogRepository;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
private Log log = LogFactory.getLog(this.getClass());
@PostMapping("/blog/add")
public JsonResult<Object> add(@Validated @RequestBody BlogModel blogModel, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<ObjectError> list = bindingResult.getAllErrors();
int len = list.size();
for (int i = 0; i < len; i++) {
FieldError fieldError = (FieldError) list.get(i);
log.error("对象 " + fieldError.getObjectName() + " 的字段 " + fieldError.getField() + ":" + fieldError.getDefaultMessage());
return new JsonResult<>("someCode",
"对象" + fieldError.getObjectName() + "的字段" + fieldError.getField() +
":" + fieldError.getDefaultMessage());
}
}
blogRepository.save(blogModel);
return new JsonResult<>();
}
@PostMapping("/blog/get/{id}")
public BlogModel getBlogById(@PathVariable String id) {
Optional<BlogModel> blogModelOptional = blogRepository.findById(id);
if (blogModelOptional.isPresent()) {
BlogModel blogModel = blogModelOptional.get();
return blogModel;
}
return null;
}
@PostMapping("/blog/update")
public String updateById(@RequestBody BlogModel blogModel) {
String id = blogModel.getId();
if (StringUtils.isEmpty(id)) {
return "缺少id";
}
blogRepository.save(blogModel);
return "success";
}
@PostMapping("/blog/delete/{id}")
public String deleteById(@PathVariable String id) {
if (StringUtils.isEmpty(id)) {
return "error";
}
blogRepository.deleteById(id);
return "success";
}
@PostMapping("/blog/search")
public JsonResult<Object> getByTitle(@RequestParam int currentPage, @RequestParam String title,
@RequestParam(required = false, defaultValue = "1") int pageSize) {
// JPA 中,page是从0开始,不是从1开始;因此,将用户输入的从1开始的page页码减1;
PageRequest pageRequest = PageRequest.of(currentPage - 1, pageSize);
Page<BlogModel> pages = blogRepository.findByTitle(title, pageRequest);
log.info(pages);
long totalElements = pages.getTotalElements();
long totalPages = pages.getTotalPages();
List<BlogModel> list = pages.getContent();
Map map = new HashMap<String, Object>();
map.put("totalElements", totalElements);
map.put("totalPages", totalPages);
map.put("pageSize", pageSize);
map.put("currentPage", currentPage);
map.put("list", list);
return new JsonResult<>(map);
}
@PostMapping("/blog/searchByTitleCustom")
public JsonResult<Object> findByTitleCustom(@RequestParam String title) {
List<BlogModel> pages = blogRepository.findByTitleCustom(title);
log.info(pages);
// long totalElements = pages.getTotalElements();
// long totalPages = pages.getTotalPages();
// List<BlogModel> list = pages.size();
// Map map = new HashMap<String, Object>();
// map.put("totalElements", totalElements);
// map.put("totalPages", totalPages);
// map.put("pageSize", pageSize);
// map.put("currentPage", currentPage);
// map.put("list", list);
return new JsonResult<>(pages);
}
@GetMapping("/blog/search/title")
public JsonResult<List<BlogModel>> searchTitle(String keyword) {
// if (StringUtils.isEmpty(keyword))
// throw new JsonResult<String>("参数不能为空");
String field = "title";
// 精确查找
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(QueryBuilders.termsQuery(field, keyword));
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.withPageable(PageRequest.of(0, 100))
.build();
// SearchQuery searchQuery = new NativeSearchQueryBuilder()
// .withQuery(QueryBuilders.queryStringQuery(keyword).defaultField(field))
// .withPageable(PageRequest.of(0, 100))
// .build();
List<BlogModel> list = elasticsearchTemplate.queryForList(searchQuery, BlogModel.class);
return new JsonResult<>(list);
}
@GetMapping("/blog/getAll")
public JsonResult getAll() {
Iterable<BlogModel> iterable = blogRepository.findAll();
List<BlogModel> list = new ArrayList<>();
iterable.forEach(list::add);
return new JsonResult<List<BlogModel>>(list);
}
}
这里说明下,一般查询主要使用的是BoolQueryBuilder的must,should等组合起来模糊或者精确查询,这里是查询的重点,有需要的请单独学习下哦。也可以看我这篇文章,BoolQueryBuilder 和 wildcardQuery 查询