SpringBoot检索Elasticsearch学习

本文最后更新于:2020年9月10日 下午

一、ElasticSearch简介及安装

1. ES的简介

简介

我们的应用经常需要添加检索功能,开源的 ElasticSearch 是目前全文搜索引擎的首选。他可以快速的存储、搜索和分析海量数据。Spring Boot通过整合Spring Data ElasticSearch为我们提供了非常便捷的检索功能支持;
Elasticsearch是一个分布式搜索服务,提供Restful API,底层基于Lucene,采用多shard(分片)的方式保证数据安全,并且提供自动resharding的功能,github等大型的站点也是采用了ElasticSearch作为其搜索服务。

概念

以员工文档的形式存储为例:一个文档代表一个员工数据。存储数据到ElasticSearch 的行为叫做 索引 ,但在索引一个文档之前,需要确定将文档存储在哪里。

一个 ElasticSearch 集群可以包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性

索引(名词):

如前所述,一个 索引 类似于传统关系数据库中的一个 数据库 ,是一个存储关系型文档的地方。 索引 (index) 的复数词为 indicesindexes

索引(动词):

索引一个文档 就是存储一个文档到一个 索引 (名词)中以便被检索和查询。这非常类似于 SQL 语句中的 INSERT 关键词,除了文档已存在时,新文档会替换旧文档情况之外。

类似关系:

  • 索引—数据库
  • 类型—表
  • 文档—表中的记录
  • 属性—列

image.png

2. ES的安装与运行

与ES交互的端口说明:

  • 9200端口

    RESTful API通过HTTP通信

  • 9300端口

    Java客户端与ES的原生传输协议和集群交互

使用docker安装

# 拉取ES镜像
docker pull elasticsearch:7.6.1
#运行ES
docker run -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name ES01 elasticsearch:7.6.1

ES_JAVA_OPTS指定java虚拟机相关参数

  • -Xms256m 初始堆内存大小为256m
  • -Xmx256m 最大堆内存大小为256m
  • discovery.type=single-node 设置为单点启动

ES默认占内存很大,默认占2G,所以,一般在启动时需要指定固定的内存大小,这里我们设置为 256M。

我们可以看到,ES已经启动了:

➜  ~ docker run -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name ES01 elasticsearch:7.6.1
7dee5f39cd0e50026cf56fa7d559fba0dd41fde81cf2d1b8a082a43213af2edc
➜  ~ docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                                            NAMES
7dee5f39cd0e        elasticsearch:7.6.1   "/usr/local/bin/dock…"   25 seconds ago      Up 24 seconds       0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp   ES01

在浏览器访问:http://localhost:9200/

返回结果:

{
    "name": "7dee5f39cd0e",
    "cluster_name": "docker-cluster",
    "cluster_uuid": "Yu2idrk6Sn6KbZE8r1-z6w",
    "version": {
        "number": "7.6.1",
        "build_flavor": "default",
        "build_type": "docker",
        "build_hash": "aa751e09be0a5072e8570670309b1f12348f023b",
        "build_date": "2020-02-29T00:15:25.529771Z",
        "build_snapshot": false,
        "lucene_version": "8.4.0",
        "minimum_wire_compatibility_version": "6.8.0",
        "minimum_index_compatibility_version": "6.0.0-beta1"
    },
    "tagline": "You Know, for Search"
}

表明我们的ES已经安装成功并可以成功访问了。

二、Springboot整合ElasticSearch

1. 概述

SpringBoot默认支持两种技术来和ES交互;

  • Jest(默认不生效)
    • 需要导入jest的工具包(io.searchbox.client.JestClient)
    • 从springboot 2.2.0以后被弃用
  • SpringData ElasticSearch

版本适配说明:

Spring Data Elasticsearch Elasticsearch
3.2.x 6.8.1
3.1.x 6.2.2
3.0.x 5.5.0
2.1.x 2.4.0
2.0.x 2.2.0
1.3.x 1.5.2

Springboot 2.2.6对应于 Spring Data Elasticsearch 3.2.6,即适配Elasticsearch 6.8.1

编写文件对应Javabean,指定索引名和类型

@Document(indexName = "ustc",type = "book")
public class Book {
    private Integer id;
    private String bookName;
    private String author;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", bookName='" + bookName + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}

配置主机地址

方式一 配置类配置

注意:这种方式底层依赖于Http相关类,因此要导入web相关jar包

@Configuration
static class Config {
  @Bean
  RestHighLevelClient client() {

    ClientConfiguration clientConfiguration = ClientConfiguration.builder() 
      .connectedTo("localhost:9200")
      .build();

    return RestClients.create(clientConfiguration).rest();                  
  }
}

方式二 spring配置文件指定

spring.elasticsearch.rest.uris=http://192.168.31.162:9200

在测试类中注入客户端

@Autowired
RestHighLevelClient highLevelClient;

创建索引

IndexRequest request = new IndexRequest("ustc", "book",
        UUID.randomUUID().toString())
        .source(Collections.singletonMap("feature", "high-level-rest-client"))
        .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
IndexResponse index = highLevelClient.index(request, RequestOptions.DEFAULT);
System.out.println(index.toString());

下面为创建索引

{
    "_index": "ustc",
    "_type": "book",
    "_id": "0dc9f47a-7913-481d-a36d-e8f034a6a3ac",
    "_score": 1,
    "_source": {
        "feature": "high-level-rest-client"
    }
}

得到索引

//分别指定要获取的索引、类型、id
GetRequest getRequest = new GetRequest("ustc","book","0dc9f47a-7913-481d-a36d-e8f034a6a3ac");
GetResponse documentFields = highLevelClient.get(getRequest, RequestOptions.DEFAULT);
System.out.println(documentFields);

2. ElasticsearchRestTemplate

ES有两个模板,分别为ElasticsearchRestTemplateElasticsearchTemplate

分别对应于High Level REST ClientTransport Client(弃用),两个模板都实现了ElasticsearchOperations接口,因此使用时我们一般使用ElasticsearchOperations,具体实现方式由底层决定。

由于在AbstractElasticsearchConfiguration中已经向容器中导入了ElasticsearchRestTemplate,因此我们使用时可以直接注入

注入模板

@Autowired
ElasticsearchOperations elasticsearchOperations;

保存索引

Book book = new Book();
book.setAuthor("路遥");
book.setBookName("平凡的世界");
book.setId(1);
IndexQuery indexQuery = new IndexQueryBuilder()
    .withId(book.getId().toString())
    .withObject(book)
    .build();
String index = elasticsearchOperations.index(indexQuery);

查询索引

Book book = elasticsearchOperations.queryForObject(GetQuery.getById("1"), Book.class);

3. Elasticsearch Repositories

编写相关Repository并继承Repository或ElasticsearchRepository,泛型分别为<查询类,主键>

public interface BookRepository extends Repository<Book,Integer> {
    List<Book> findByBookNameAndAuthor(String bookName, String author);
}

查询的方法仅需按照一定规则命名即可实现功能,无需编写实现,如上findByBookNameAndAuthor()方法相当于ES的json查询

{
    "query": {
        "bool" : {
            "must" : [
                { "query_string" : { "query" : "?", "fields" : [ "bookName" ] } },
                { "query_string" : { "query" : "?", "fields" : [ "author" ] } }
            ]
        }
    }
}

更多命名规则见本文档

@Query

此外,还可以使用@Query自定义请求json

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("{\"match\": {\"name\": {\"query\": \"?0\"}}}")
    Page<Book> findByName(String name,Pageable pageable);
}

若参数为John,相当于请求体为

{
  "query": {
    "match": {
      "name": {
        "query": "John"
      }
    }
  }
}

更多ES与springboot整合内容见官方文档