Spring Boot与检索

ElasticSearch

检索

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

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

概念

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

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

类似关系:
– 索引-数据库
– 类型-表
– 文档-表中的记录
– 属性-列

打开官网 ElasticSearch

《Elasticsearch: 权威指南》中文版

安装ElasticSearch

打开我们的虚拟机,下载ElasticSearch镜像

docker pull registry.docker-cn.com/library/elasticsearch

elasticsearch:默认是使用2g的堆内存,直接启动的话,我们的虚拟机有可能不够,所以,我们需要设置elasticsearch的堆内存的大小:

使用 -e ES_JAVA_OPTS=”-Xms256m -Xmx256m” 来设置堆的最大、最小都是256兆。

完整的启动命令:
elasticsearch的默认端口号是9200,和分布式的节点之间通信的端口号:9300

docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name ESO1 elasticsearch的镜像id

打开浏览器,输入:http://10.6.11.17:9200/

以上,说明ElasticSearch启动成功!


ElasticSearch简单操作

ElasticSearch:使用 RESTful API 通过端口 9200 进行通信,使用 JavaScript Object Notation 或者 JSON 作为文档的序列化格式。

考虑一下这个 JSON 文档,它代表了一个 user 对象:


打开我们的postman工具

在Elasticsearch: 权威指南的基础入门中:适应新环境

例子为:创建一个雇员目录

对于雇员目录,我们将做如下操作:

  • 每个雇员索引一个文档,包含该雇员的所有信息。

  • 每个文档都将是 employee 类型 。

  • 该类型位于 索引 megacorp 内。

  • 该索引保存在我们的 Elasticsearch 集群中。

实践中这非常简单(尽管看起来有很多步骤),我们可以通过一条命令完成所有这些动作:

创建雇员信息

在postman中:输入 10.6.11.17:9200/megacorp/employee/1

选择:PUT请求 –> Body –> raw –> JSON:

{
    "first_name" : "John",
    "last_name" :  "Smith",
    "age" :        25,
    "about" :      "I love to go rock climbing",
    "interests": [ "sports", "music" ]
}

点击Send发送:
响应数据:

{
    "_index": "megacorp",
    "_type": "employee",
    "_id": "1",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "created": true
}

以上:我们就插入数据成功了。

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

megacorp:索引名称

employee:类型名称

1:特定雇员的ID

请求体 —— JSON 文档 —— 包含了这位员工的所有详细信息,他的名字叫 John Smith ,今年 25 岁,喜欢攀岩。

很简单!无需进行执行管理任务,如创建一个索引或指定每个属性的数据类型之类的,可以直接只索引一个文档。Elasticsearch 默认地完成其他一切,因此所有必需的管理任务都在后台使用默认设置完成。

进行下一步前,让我们增加更多的员工信息到目录中:


简单检索

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

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

GET /megacorp/employee/1

返回结果包含了文档的一些元数据,以及 _source 属性,内容是 John Smith 雇员的原始 JSON 文档:

{
  "_index" :   "megacorp",
  "_type" :    "employee",
  "_id" :      "1",
  "_version" : 1,
  "found" :    true,
  "_source" :  {
      "first_name" :  "John",
      "last_name" :   "Smith",
      "age" :         25,
      "about" :       "I love to go rock climbing",
      "interests":  [ "sports", "music" ]
  }
}

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

轻量检索

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

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

GET /megacorp/employee/_search

可以看到,我们仍然使用索引库 megacorp 以及类型 employee,但与指定一个文档 ID 不同,这次使用 _search 。返回结果包括了所有三个文档,放在数组 hits 中。一个搜索默认返回十条结果。

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

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

GET /megacorp/employee/_search?q=last_name:Smith

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


使用查询表达式搜索

Query-string 搜索通过命令非常方便地进行临时性的即席搜索 ,但它有自身的局限性(参见 轻量 搜索 )。Elasticsearch 提供一个丰富灵活的查询语言叫做 查询表达式 , 它支持构建更加复杂和健壮的查询。

领域特定语言 (DSL), 指定了使用一个 JSON 请求。我们可以像这样重写之前的查询所有 Smith 的搜索

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "last_name" : "Smith"
        }
    }
}

因为 GET请求,无法带这样格式的json请求参数,所以我们使用POST请求即可:

返回结果与之前的查询一样,但还是可以看到有一些变化。其中之一是,不再使用 query-string 参数,而是一个请求体替代。这个请求使用 JSON 构造,并使用了一个 match 查询(属于查询类型之一,后续将会了解)。

更多的查询操作,请看ElasticSearch的官方文档即可https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html

整合Springboot与ElasticSearch

1)创建Springboot的项目,添加web、ElasticSearch模块:


2)在autoconfig包下:

发现有两处ElasticSearch:

  1. data包下,有一个ElasticSearch

  2. 直接在ElasticSearch包,有一个jest包

即:Springboot提供了两种方式来使用ElasticSearch。

data包下的ElasticSearch是Springboot默认提供的。

jest默认不支持的,需要jest的工具包(io.searchbox.client.JestClient)

使用jest

1)注释掉:spring-boot-starter-data-elasticsearch

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

添加jest的依赖包:
在mave中央仓库:搜索:jest

因为我的ElasticSearch的版本是:5.6.12

所以,jest的版本选择5.x.x的即可:

<!-- https://mvnrepository.com/artifact/io.searchbox/jest -->
<dependency>
    <groupId>io.searchbox</groupId>
    <artifactId>jest</artifactId>
    <version>5.3.3</version>
</dependency>

2) 打开:JestAutoConfiguration类

导入了一个:jestClient类

    @Bean(destroyMethod = "shutdownClient")
    @ConditionalOnMissingBean
    public JestClient jestClient() {
        JestClientFactory factory = new JestClientFactory();
        factory.setHttpClientConfig(createHttpClientConfig());
        return factory.getObject();
    }

3)给application配置文件中,添加ElasticSearch的相关配置:

查看JestProperties类,了解需要配置哪些信息:

application:

#ElasticSearch的相关配置
spring.elasticsearch.jest.uris=http://10.6.11.17:9200  #虚拟机的ip地址

4) 启动Springboot应用:

5)在测试类中测试:

6)编写一个POJO类:

在bean包,创建一个Article类:

public class Article {

    //标记id为ElasticSearch中的id
    @JestId
    private Integer id;
    private String author;
    private String title;
    private String content;

    set 和 get 方法
}

7)创建一个测试方法:

    @Test
    public void testInsert(){

        Article article = new Article();
        article.setId(1);
        article.setAuthor("zhangsan");
        article.setTitle("好消息");
        article.setContent("Hello world!");

        //创建一个:索引:liuzhuo , 类型:news ,id:使用了@JestId标记了id,这里可以不用设置了
        Index index = new Index.Builder(article).index("liuzhuo").type("news").build();

        try {
            //执行
            jestClient.execute(index);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

执行testInsert()方法后:

在浏览器中,输入:http://10.6.11.17:9200/liuzhuo/news/1

插入数据成功!


现在,测试搜索功能:

使用:搜索content内容为:hello

{
    "query" : {
        "match" : {
            "content" : "hello"
        }
    }
}

    @Test
    public void search(){
        String json ="{\n" +
                "    \"query\" : {\n" +
                "        \"match\" : {\n" +
                "            \"content\" : \"hello\"\n" +
                "        }\n" +
                "    }\n" +
                "}";
        //构建搜索功能
        Search search = new Search.Builder(json).addIndex("liuzhuo").addType("news").build();
        try {
            SearchResult result = jestClient.execute(search);
            System.out.println(result.getJsonString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

运行测试:search()方法:

更多:jest的操作,可以看jest的文档。

打开github的官网,搜索 jest


使用SpringData

1)在pom文件中:放开spring-boot-starter-data-elasticsearch的注释

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

打开:ElasticsearchAutoConfiguration类:

再看:ElasticsearchDataAutoConfiguration类:

导入了一个:ElasticsearchTemplate 来操作ElasticSearch的模板类

使用:ElasticsearchProperties 来配置相关信息:

Client:节点的信息 clusterName:名字,clusterNodes:操作的节点ip地址

编写了一个ElasticSearchRepository的子接口来操作ES。

2)在application配置文件中:添加SpringData的ES信息

#使用SprigData的ES配置信息
spring.data.elasticsearch.cluster-name=elasticsearch #默认也elasticsearch
#使用9300端口通信,与jest不同!!!
spring.data.elasticsearch.cluster-nodes=10.6.11.17:9300

spring.data.elasticsearch.cluster-name:怎么确定的呢?

在浏览器中输入:http://10.6.11.17:9200/

3) 启动Springboot应用:

出现了异常!!!异常错误,连接超时!!!

why? 这是因为我们的ElasticSearch是5.6.12,而导入的SpringData的ElasticSearch依赖是2.4.6

版本不一致产生的异常。

怎样确定 SpringData-ElasticSearch 与 ElasticSearch 的版本对应关系呢?

打开Spring的官网,找到Spring data模块




所以,现在有两种方案:

1)升级Springboot的版本,将Springboot升级到3.0.x以后。

2)下载2.x.x的ElasticSearc

我这里选择下载第二种方案:

打开docker的hub仓库,搜索ElasticSearch

打开虚拟机的客户端:

输入:

docker pull registry.docker-cn.com/library/elasticsearch:2.4.6

启动 2.4.6 版本的ElasticSearch。

因为 9200 和 9300 的docker端口号已经被 5版本的ElasticSearch占领了,所以选择 9201 和 9301端口号

docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9201:9200 -p 9301:9300 --name ESO2 elasticsearch的镜像id

在浏览器中输入:http://10.6.11.17:9201/

以上,2.4.6 的ElasticSearch启动成功。

4)修改application配置文件:

#ElasticSearch的相关配置
spring.elasticsearch.jest.uris=http://10.6.11.17:9200

#使用SprigData的ES配置信息
spring.data.elasticsearch.cluster-name=elasticsearch
#使用9300端口通信,与jest不同!!!
spring.data.elasticsearch.cluster-nodes=10.6.11.17:9301

5)启动Springboot应用:

两种使用ElasticSearch的配置,都启动成功了。现在我们只使用SpringData操作ElasticSearch的方式。

SpringData:提供了两种方式来操作ElasticSearc:

1)使用 ElasticsearchRepository

2)使用 ElasticsearchTemplate


6)使用 ElasticsearchRepository

在bean包下,创建Book类:

在repository包下,创建bookRepository接口,继承ElasticsearchRepository:

在test类下,注入bookRepository

但是,发现index方法,没法设置 索引,类型,id。其实我们需要在Book类上,添加@Document注解,来设置索引和类型

@Document(indexName = "liuzhuo",type = "book")
public class Book {

启动测试方法:

在浏览器中输入:http://10.6.11.17:9201/liuzhuo/book/_search 查询索引是liuzhuo,类型是book的所有数据:

修改测试方法:

    @Test
    public void test() {
        Book book = new Book();
        book.setId(1);
        book.setBookName("西游记");
        book.setAuthor("罗贯中");
        bookRepository.index(book);
    }

运行测试方法:

我们还有可以直接在BookRepository接口中,编写接口,就和JPA一样:

//泛型上面的参数:查找的类型 和 id的类型
public interface BookRepository extends ElasticsearchRepository<Book,Integer> {

    //根据book的名字来模糊匹配所有的book
    public List<Book> findByBookNameLike(String bookName);

}
    @Test
    public void searchBook(){
        List<Book> bookList = bookRepository.findByBookNameLike("游");
        for (Book book : bookList) {
            System.out.println(book);
        }
    }

更多用法:请看官方文档:https://docs.spring.io/spring-data/elasticsearch/docs/2.1.16.RELEASE/reference/html/


  转载请注明: 解忧杂货店 Spring Boot与检索

  目录