Spring Boot整合Elasticsearch,最新最全教程

要想快速入门新技术,英文文档的阅读能力必不可少。本篇文章主要参考《Spring Data Elasticsearch》Spring Boot整合Elasticsearch的官方文档。
Elasticsearch官网参考文档:https://www.elastic.co/guide/index.html
Elasticsearch官方下载地址:https://www.elastic.co/cn/downloads/elasticsearch
Elasticsearch目前最近版本为:7.10.1

最近系统中要用到搜索功能,如果直接使用MySQL的like查询语句会影响系统的性能,所以就采用了Elasticsearch来实现站内搜索。本文以图书的搜索功能作为Demo来演示在Spring Boot如何整合Elasticsearch以及如何优雅的使用elasticsearch。

Spring Boot与Elasticsearch的对应版本

Elasticsearch更新非常快,最新的7.x版本已经不支持自定义类型了,默认以**"_doc"**作为类型,一个索引只能有一个类型。
为了避免使用的Elasticsearch版本和SpringBoot采用的版本不一致导致的问题,尽量使用一致的版本。下表是对应关系:
对应关系

创建SpringBoot项目并引入Elasticsearch依赖

本文使用的SpringBoot版本为2.3.7.RELEASE
对应的Elasticsearch版本为7.6.2

引入Spring-Data-Elasticsearch依赖,Spring 团队将Elasticsearch归到“Data”的范畴,所以依赖是以Spring-Data开头。

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

这个例子中还使用了JPA,所以需要引入JPA依赖:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

基于Java的Elasticsearch配置

新建类 RestClientConfig作为Elasticsearch Rest客户端的配置了类:

/**
 * ElasticSearch 客户端配置
 *
 * @author geng
 * 2020/12/19
 */
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("localhost:9200")
                .build();
        return RestClients.create(clientConfiguration).rest();
    }
}

注意:Elasticsearch从7版本开始TransportClient已经过时了不再推荐使用,将在8.0版本删除,具体参考https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/transport-client.html。Spring Data Elasticsearch支持TransportClient,只要它在已使用的Elasticsearch版本中可用,但从4.0版本起已经不再建议使用它。

强烈建议使用Hight Level Rest Client(也就是上边的配置)代替TransportClient。High Level REST Client参考文档

像操作普通数据库一样实现Elasticsearch的CRUD

说真的看到这,你是不是惊呆了,反正我是!Spring Boot提供高级接口,操作ElasticSearch就像写数据库的CRUD一样简单!大佬,请收下我的膝盖!

编码流程和数据库CRUD一样:

定义Book实体,官方叫Elasticsearch 对象映射:

import lombok.Data;
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;
/**
 * @author geng
 * 2020/12/18
 */
@Data
@Document(indexName = "book",createIndex = true)
public class Book {
    @Id
    @Field(type = FieldType.Text)
    private String id;
    @Field(analyzer="ik_max_word")
    private String title;
    @Field(analyzer="ik_max_word")
    private String author;
    @Field(type = FieldType.Double)
    private Double price;
    @Field(type = FieldType.Date,format = DateFormat.basic_date_time)
    private Date createTime;
    @Field(type = FieldType.Date,format = DateFormat.basic_date_time)
    private Date updateTime;
}
  • @Document定义在Elasticsearch中索引信息
  • @Id定义了Elasticsearch的_id
  • @Field定义字段类型等信息
  • …更多注解请参照官方文档…

Elasticsearch Repositories

创建接口ESBookRepository

import com.gyb.elasticsearch.demo.entity.es.Book;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;

/**
 * ES Book repository
 *
 * @author geng
 * 2020/12/19
 */
public interface ESBookRepository extends ElasticsearchRepository<Book, String> {

    List<Book> findByTitleOrAuthor(String title, String author);

    @Highlight(fields = {
            @HighlightField(name = "title"),
            @HighlightField(name = "author")
    })
    @Query("{\"match\":{\"title\":\"?0\"}}")
    SearchHits<Book> find(String keyword);
}

该接口继承了ElasticsearchRepository接口,ElasticsearchRepository接口定义了Elasticsearch的CRUD,继承了该接口的接口甚至无需定义任何其他的方法就能满足基本需求。
比较厉害的是通过定义的方法名就能自动创建各种查询,例如:

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/**
 * 关系型数据库mysql Repository
 *
 * @author cloudgyb
 * @since 2022/3/19 19:31
 */
public interface BookRepository extends JpaRepository<Book, String> {
    List<Book> findByNameAndPrice(String name, Integer price);
}

这个方法将自动被转化成下面的Elasticsearch Json查询语句

{
    "query": {
        "bool" : {
            "must" : [
                { "query_string" : { "query" : "?", "fields" : [ "name" ] } },
                { "query_string" : { "query" : "?", "fields" : [ "price" ] } }
            ]
        }
    }
}

好NB有没有?
并且支持使用@Query注解自定义查询,使用@Highlight定义关键字高亮,没有一个注解解决不了的问题。

方法名支持的关键字如下:
在这里插入图片描述
在这里插入图片描述
这只是部分,更多查看官方文档。

并且方法支持多种返回类型,无需做任何适配,根据类型自动封装:

  • List
  • Stream
  • SearchHits
  • List<SearchHit>
  • Stream<SearchHit>
  • SearchPage

定义Service方法

/**
 * @author geng
 * 2020/12/19
 */
@Slf4j
@Service
public class BookService {
    private final BookRepository bookRepository;
    private final ESBookRepository esBookRepository;
    private final TransactionTemplate transactionTemplate;

    public BookService(BookRepository bookRepository,
                       ESBookRepository esBookRepository,
                       TransactionTemplate transactionTemplate) {
        this.bookRepository = bookRepository;
        this.esBookRepository = esBookRepository;
        this.transactionTemplate = transactionTemplate;
    }

    public void addBook(Book book) {
        final Book saveBook = transactionTemplate.execute((status) ->
                bookRepository.save(book)
        );
        final com.gyb.elasticsearch.demo.entity.es.Book esBook = new com.gyb.elasticsearch.demo.entity.es.Book();
        assert saveBook != null;
        BeanUtils.copyProperties(saveBook, esBook);
        esBook.setId(saveBook.getId() + "");
        try {
            esBookRepository.save(esBook);
        }catch (Exception e){
            log.error(String.format("保存ES错误!%s", e.getMessage()));
        }
    }

    public List<com.gyb.elasticsearch.demo.entity.es.Book> searchBook(String keyword){
        return esBookRepository.findByTitleOrAuthor(keyword, keyword);
    }

    public SearchHits<com.gyb.elasticsearch.demo.entity.es.Book> searchBook1(String keyword){
        return esBookRepository.find(keyword);
    }
}

定义Controller


/**
 * @author geng
 * 2020/12/20
 */
@RestController
public class BookController {
    private final BookService bookService;
    
    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    @PostMapping("/book")
    public Map<String,String> addBook(@RequestBody Book book){
        bookService.addBook(book);
        Map<String,String> map = new HashMap<>();
        map.put("msg","ok");
        return map;
    }

    @GetMapping("/book/search")
    public SearchHits<com.gyb.elasticsearch.demo.entity.es.Book> search(String key){
        return bookService.searchBook1(key);
    }
}

测试看效果

1、启动Elasticsearch服务

无需手动创建Book索引,Springboot启动自动创建

2、启动SpringBoot项目

3、使用/book接口创建几条测试数据

curl -H "Content-Type:application/json" -XPOST http://localhost:8080/book -d
{
    "id":"1",
    "title":"富婆通讯录",
    "author":"耿小李",
    "price":9999.9,
    "createTime":"2020-12-21 12:00:00"
}

4、搜索数据

打开浏览器http://localhost:8080/book/search?key=富婆:

在这里插入图片描述

Spring Data Elasticsearch官方文档

GITHUB仓库源码:https://github.com/cloudgyb/es-spring-boot


版权声明:本文为gybshen原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。