SpringBoot 集成 elasticsearch 实现精确查询

Mac下安装ElasticSearch 6.6.2

MAC下安装ElasticSearch Head插件

elasticsearch IK 中文分词器 精确查询

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 查询

二. ES数据测试 

参考文章:https://segmentfault.com/a/1190000018625101 

猜你喜欢

转载自blog.csdn.net/robinson_911/article/details/105832068