文章目录

  • 十四、检索
  • 1.概论
  • 2.搭建环境(docker)
  • 3.概念
  • 4.向ES端口发送json数据
  • 案例
  • (1)存储雇员数据
  • (2)检索雇员数据
  • (3)轻量搜索
  • 5.整合ElasticSearch测试
  • (1)搭建环境
  • (2)ES交互
  • <1>.Jest(默认不生效)
  • <2>.SpringData ElasticSearch
  • 编写一个ElasticSearchRepository


十四、检索

1.概论

我们的应用经常需要添加检索功能,开源的 ElasticSearch 是目前全文搜索引擎的首选。他可以快速的存储、搜索和分析海量数据。Spring Boot通过整合Spring Data ElasticSearch为我们提供了非常便捷的检索功能支持;

Elasticsearch是一个分布式搜索服务,提供Restful API,底层基于Lucene,采用多shard(分片)的方式保证数据安全,并且提供自动resharding的功能,github等大型的站点也是采用了ElasticSearch作为其搜索服务,

Elasticsearch 是 面向文档 的,意味着它存储整个对象或 文档_。Elasticsearch 不仅存储文档,而且 _索引 每个文档的内容使之可以被检索。在 Elasticsearch 中,你 对文档进行索引、检索、排序和过滤–而不是对行列数据。这是一种完全不同的思考数据的方式,也是 Elasticsearch 能支持复杂全文检索的原因。

2.搭建环境(docker)

[root@localhost ~]# docker search elasticsearch
INDEX       NAME                                            DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
docker.io   docker.io/elasticsearch                         Elasticsearch is a powerful open source se...   3531      [OK]       
docker.io   docker.io/nshou/elasticsearch-kibana            Elasticsearch-6.5.4 Kibana-6.5.4                96                   [OK]
docker.io   docker.io/itzg/elasticsearch                    Provides an easily configurable Elasticsea...   67                   [OK]
docker.io   docker.io/elastichq/elasticsearch-hq            Official Docker image for ElasticHQ: Elast...   26                   [OK]
docker.io   docker.io/kubernetes/fluentd-elasticsearch      An image that ingests Docker container log...   25                   
docker.io   docker.io/lmenezes/elasticsearch-kopf           elasticsearch kopf                              17                   [OK]
docker.io   docker.io/bitnami/elasticsearch                 Bitnami Docker Image for Elasticsearch          16                   [OK]
docker.io   docker.io/taskrabbit/elasticsearch-dump         Import and export tools for elasticsearch       15                   [OK]
docker.io   docker.io/elastic/elasticsearch                 The Elasticsearch Docker image maintained ...   14                   
docker.io   docker.io/esystemstech/elasticsearch            Debian based Elasticsearch packing for Lif...   14                   
docker.io   docker.io/monsantoco/elasticsearch              ElasticSearch Docker image                      11                   [OK]
docker.io   docker.io/mesoscloud/elasticsearch              [UNMAINTAINED] Elasticsearch                    9                    [OK]
docker.io   docker.io/blacktop/elasticsearch                Alpine Linux based Elasticsearch Docker Image   8                    [OK]
docker.io   docker.io/justwatch/elasticsearch_exporter      Elasticsearch stats exporter for Prometheus     8                    
docker.io   docker.io/centerforopenscience/elasticsearch    Elasticsearch                                   4                    [OK]
docker.io   docker.io/barchart/elasticsearch-aws            Elasticsearch AWS node                          3                    
docker.io   docker.io/forkdelta/fluentd-elasticsearch       fluent/fluentd with fluent-plugin-elastics...   1                    [OK]
docker.io   docker.io/jetstack/elasticsearch-pet            An elasticsearch image for kubernetes PetSets   1                    [OK]
docker.io   docker.io/phenompeople/elasticsearch            Elasticsearch is a powerful open source se...   1                    [OK]
docker.io   docker.io/18fgsa/elasticsearch                  Built from https://github.com/docker-libra...   0                    
docker.io   docker.io/axway/elasticsearch-docker-beat       "Beat" extension to read logs of container...   0                    [OK]
docker.io   docker.io/driveclutch/infra-elasticsearch-aws   Elasticsearch Docker for use in AWS             0                    [OK]
docker.io   docker.io/igneoussystems/base-elasticsearch     A base image containing 3p tools for elast...   0                    
docker.io   docker.io/igneoussystems/base-elasticsearch-5   Base elasticsearch 5.1.1 container              0                    
docker.io   docker.io/wreulicke/elasticsearch               elasticsearch                                   0                    [OK]

[root@localhost ~]# docker pull registry.docker-cn.com/library/elasticsearch
Using default tag: latest
Trying to pull repository registry.docker-cn.com/library/elasticsearch ... 
latest: Pulling from registry.docker-cn.com/library/elasticsearch
05d1a5232b46: Pull complete 
5cee356eda6b: Pull complete 
89d3385f0fd3: Pull complete 
65dd87f6620b: Pull complete 
78a183a01190: Pull complete 
1a4499c85f97: Pull complete 
2c9d39b4bfc1: Pull complete 
1b1cec2222c9: Pull complete 
59ff4ce9df68: Pull complete 
1976bc3ee432: Pull complete 
5af49e8af381: Pull complete 
42c8b75ff7af: Pull complete 
7e6902915254: Pull complete 
99853874fa54: Pull complete 
596fbad6fcff: Pull complete 
Digest: sha256:a8081d995ef3443dc6d077093172a5931e02cdb8ffddbf05c67e01d348a9770e
Status: Downloaded newer image for registry.docker-cn.com/library/elasticsearch:latest

[root@localhost ~]# docker images
REPOSITORY                                     TAG                 IMAGE ID            CREATED             SIZE
docker.io/rabbitmq                             3-management        20c898989d5c        37 hours ago        213 MB
docker.io/tomcat                               latest              02eaee4dc65c        3 days ago          463 MB
docker.io/redis                                latest              a55fbf438dfd        4 days ago          95 MB
docker.io/mysql                                latest              7bb2586065cd        4 days ago          477 MB
registry.docker-cn.com/library/elasticsearch   latest              5acf0e8da90b        6 months ago        486 MB

[root@localhost ~]# docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name ES01 5acf0e8da90b
c2a8a49519b42a3035bab87d6107464aa5e83e4ae9fea06eed0545f7b7504a55

[root@localhost ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                                                        NAMES
c2a8a49519b4        5acf0e8da90b        "/docker-entrypoin..."   4 seconds ago       Up 3 seconds        0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp                                               ES01
6d58aafede2d        20c898989d5c        "docker-entrypoint..."   22 hours ago        Up 9 hours          4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp   myrabbitmq

在这里提一句,因为ES默认启动会占用2G的运行内存,所以需要手动去修改最小占用内存
然后我们来到ES对应的Web见面,来检验安装

{
  "name" : "h6idySx",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "MCg7KwdRSAmAzVSbuBseAQ",
  "version" : {
    "number" : "5.6.12",
    "build_hash" : "cfe3d9f",
    "build_date" : "2018-09-10T20:12:43.732Z",
    "build_snapshot" : false,
    "lucene_version" : "6.6.1"
  },
  "tagline" : "You Know, for Search"
}

3.概念

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

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

类似关系:

索引-数据库

类型-表

文档-表中的记录

属性-列

springboot es多索引 springboot 搜索_Elastic

4.向ES端口发送json数据

为了更加了解ES对于面向文档的存储特性和搜索功能,所以我们借助postman来完成json的数据注入和查询,因为json的特点就是跨平台和轻量级。

首先我们连接到我们的9200端口。

springboot es多索引 springboot 搜索_springboot es多索引_02

案例

发送成功。然后进行一个官网文档上的小案例:https://www.elastic.co/guide/cn/elasticsearch/guide/current/_finding_your_feet.html

我们受雇于 Megacorp 公司,作为 HR 部门新的 “热爱无人机” (“We love our drones!”)激励项目的一部分,我们的任务是为此创建一个雇员目录。该目录应当能培养雇员认同感及支持实时、高效、动态协作,因此有一些业务需求:

  • 支持包含多值标签、数值、以及全文本的数据
  • 检索任一雇员的完整信息
  • 允许结构化搜索,比如查询 30 岁以上的员工
  • 允许简单的全文搜索以及较复杂的短语搜索
  • 支持在匹配文档内容中高亮显示搜索片段
  • 支持基于数据创建和管理分析仪表盘
(1)存储雇员数据

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

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

思路:

  • 每个雇员索引一个文档,包含该雇员的所有信息。
  • 每个文档都将是 employee 类型 。
  • 该类型位于 索引 megacorp 内。
  • 该索引保存在我们的 Elasticsearch 集群中。

springboot es多索引 springboot 搜索_elasticSearch_03

注意,路径 /megacorp/employee/1 包含了三部分的信息:

megacorp
索引名称
employee
类型名称
1
特定雇员的ID

springboot es多索引 springboot 搜索_springboot es多索引_04


加入更多的雇员信息,然后再进行检索操作

(2)检索雇员数据

目前我们已经在 Elasticsearch 中存储了一些数据, 接下来就能专注于实现应用的业务需求了。第一个需求是可以检索到单个雇员的数据。

这在 Elasticsearch 中很简单。简单地执行 一个 HTTP GET 请求并指定文档的地址——索引库、类型和ID。 使用这三个信息可以返回原始的 JSON 文档:

springboot es多索引 springboot 搜索_Elastic_05

将 HTTP 命令由 PUT 改为 GET 可以用来检索文档,同样的,可以使用 DELETE 命令来删除文档,以及使用 HEAD 指令来检查文档是否存在。如果想更新已存在的文档,只需再次 PUT

- 没删之前先HEAD一下,发现status是200

springboot es多索引 springboot 搜索_elasticsearch_06

  • 发送delete请求的时候,也是200,说明删除成功
  • 删除之后再次查看Status变成了404
(3)轻量搜索

一个 GET 是相当简单的,可以直接得到指定的文档。 现在尝试点儿稍微高级的功能,比如一个简单的搜索!

第一个尝试的几乎是最简单的搜索了。我们使用下列请求来搜索所有雇员:
我们像这样输入

http://192.168.243.30:9200/megacorp/employee/_search

选择GET请求,然后点击Send
然后就会相应出所有雇员的信息了

{
    "took": 58,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 3,
        "max_score": 1,
        "hits": [
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "2",
                "_score": 1,
                "_source": {
                    "first_name": "Jane",
                    "last_name": "Smith",
                    "age": 32,
                    "about": "I like to collect rock albums",
                    "interests": [
                        "music"
                    ]
                }
            },
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "1",
                "_score": 1,
                "_source": {
                    "first_name": "John",
                    "last_name": "Smith",
                    "age": 25,
                    "about": "I love to go rock climbing",
                    "interests": [
                        "sports",
                        "music"
                    ]
                }
            },
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "3",
                "_score": 1,
                "_source": {
                    "first_name": "Douglas",
                    "last_name": "Fir",
                    "age": 35,
                    "about": "I like to build cabinets",
                    "interests": [
                        "forestry"
                    ]
                }
            }
        ]
    }
}

注意:返回结果不仅告知匹配了哪些文档,还包含了整个文档本身:显示搜索结果给最终用户所需的全部信息。

接下来,尝试下搜索姓氏为 Smith 的雇员。为此,我们将使用一个 高亮 搜索,很容易通过命令行完成。这个方法一般涉及到一个 查询字符串 (query-string) 搜索,因为我们通过一个URL参数来传递查询信息给搜索接口:

springboot es多索引 springboot 搜索_Elastic_07


我们仍然在请求路径中使用 _search 端点,并将查询本身赋值给参数 q= 。返回结果给出了所有的 Smith:

后面等等的细节操作可以去跟随官网的教程去学习

5.整合ElasticSearch测试

(1)搭建环境

springboot es多索引 springboot 搜索_springboot es多索引_08

配置我们ES的端口号在properties文件中

spring.elasticsearch.jest.uris=http://192.168.243.30:9200

(2)ES交互

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

<1>.Jest(默认不生效)

需要导入Jest的工具包(io.searchbox.client.JestClient)
所以我们来导入Jest相关的maven配置程序,然后将SpringData的maven打上注释

<dependency>
            <groupId>io.searchbox</groupId>
            <artifactId>jest</artifactId>
            <version>5.3.4</version>
        </dependency>

由于我们的ES是5.6.12版本,所以Jest的版本也应该是5.X
然后测试程序,为了让测试类方便运行,所以我们先为ES索引一个文档,方便进行操作。

package springboot.elastic.bean;

import io.searchbox.annotations.JestId;

public class Article {

    @JestId
    private Integer id;
    private String author;
    private String title;
    private String content;

    public Integer getId() {
        return id;
    }

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

    public String getAuthor() {
        return author;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

然后首先为我们的ES文档创建一个索引

@Autowired
    JestClient jestClient;
    @Test
    public void contextLoads() {
        //1.给ES索引一个文档
        Article article = new Article();
        article.setAuthor("zz");
        article.setId(1);
        article.setContent("zz is sb");
        article.setTitle("a z");

//        构建一个索引功能
        Index build = new Index.Builder(article).index("zzsb").type("news").build();
//         save ES.build project.指定索引.指定类型

        try {
            jestClient.execute(build);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

运行程序,来到对应端口

springboot es多索引 springboot 搜索_springboot es多索引_09


数据操作成功。

然后使用Jest来进行一下搜索数据的操作

@Test
    public void search(){
        //查询表达式
        String json = "{\n" +
                "\t\"query\" : {\n" +
                "\t\t\"match\" :{\n" +
                "\t\t\t\"content\" : \"hello\"\n" +
                "\t\t}\n" +
                "\t}\n" +
                "}";
//        构建搜索
        Search build = new Search.Builder(json).addIndex("zzsb").addType("news").build();

//        执行
        try {
            SearchResult execute = jestClient.execute(build);
            System.out.println(execute.getJsonString());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

然后进行测试

springboot es多索引 springboot 搜索_elasticSearch_10

<2>.SpringData ElasticSearch

首先把原来的SpringData的注释消除,然后在配置类中进行配置

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=192.168.243.30:9300

然后运行,将会出现版本不适配的问题,说连接拒绝。
解决方法有两个,第一个就是提升SpringBoot的版本,第二就是降低ES的版本。
这里选择第二个,

springboot es多索引 springboot 搜索_elasticsearch_11


由于我的ES是2.4.6,所以来到docker重新下载2.4.6版本的ES,操作步骤就省略了。

然后Properties的端口号也改成9301,重新运行,发现成功。

1)Client节点信息clusterNodes;clusterName
2)Elasticsearchtemplate 操作es
3)编写一个ElasticSearchRepository的子接口来操作es

这里将会说明SpringData ES的两种用法

编写一个ElasticSearchRepository

首先创建一个这样的接口:

public interface BookRepository extends ElasticsearchRepository {

}

我们发现他内部会需要两个泛型,都是和我们操作的数据源相关的,所以我们先来创建我们的数据源。

package springboot.elastic.bean;

import org.springframework.data.elasticsearch.annotations.Document;

@Document(indexName = "zzsb",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) {
        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 + '\'' +
                '}';
    }
}

在这里呢,我们的Book是被定义为文档的,所以要加上document的注解,然后声明类型和索引
然后就可以返回我们的接口类,重新进行编写

public interface BookRepository extends ElasticsearchRepository<Book,Integer> {

}

Book是我们要操作的主键,而Integer是主键类型。
来到我们的测试类,进行测试

@Autowired
    BookRepository bookRepository;

    @Test
    public void test(){
        Book book = new Book();
        bookRepository.index(book);
    }

然后运行程序,来到我们定义的9201端口
运行成功

{"took":4,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":1.0,"hits":[{"_index":"zzsb","_type":"book","_id":"null","_score":1.0,"_source":{"id":null,"author":null,"bookName":null}}]}}

现在我们已经能成功连接到平台了,下面要做的就是填充数据了。

public void test(){
        Book book = new Book();
        book.setId(1);
        book.setBookName("zzBook");
        book.setAuthor("zz");
        bookRepository.index(book);
    }

重新运行

{"_index":"zzsb","_type":"book","_id":"1","found":false}

运行成功