学而实习之 不亦乐乎

SpringBoot 集成 Elasticsearch 实现全文搜索

2024-02-25 20:40:53

一、安装 Elasticsearch

1、所需环境

Elasticsearch 5.5.0

Spring Data Elasticsearch 3.0.0.M4

2、build.gradle

我们需要添加 Spring Data Elasticsearch 的依赖 Spring Boot 提供了相关的 Starter 来实现 Spring Data Elasticsearch 开箱即用的功能,所以只需要在 build.gradle 文件中添加 Spring Data Elasticsearch Starter 的库即可。

// 依赖关系
dependencies {
    // ...
    // 添加Spring Data Elasticsearch 的依赖
    compile (’ org.springframework.boot:spring-boot-starter-data-elastic￾search ’)

    // ...
}

3、下载并安装 Elasticsearch

下载并解压,有一个名为 elasticsearch-5.5.0 的文件 ,将其称为%ES_HOME% 变量 在终端窗口中,切换到 %ES HOME%

例如,

> cd d:\elasticsearch-5.5.0

运行与停止服务

> .\bin\elasticsearch
... ...

可以输入 Ctr +C 来停止服务

4、配置

默认情况下,/config/elasticsearch.yml 文件加载其配置。也可以使用 -E 语法指定能在配置文件中指定的任何设置, 如下所

./bin/elasticsearch -Ecluster.name=my_cluster -Enode.name=node_1

注意:包含空格的值必须加上双引号,例如,-Epath.logs="C:\My Logs\logs".

提示:通常,任何集群范围的设置(如 cluster.name)都应该添加到 elasticsearch.yml 文件中,而任何特定于节点的设置(如 node.name )都可以在命令行中指定。

5、确认运行情况

可以通过向 localhost 上的端口 9200 发送 HTTP GET 请求来测试 Elasticsearch 节点是否正在运行。如下:

http://localhost:9200/

6、作为 Windows 服务

执行 bin 目录下的 elasticsearch service.bat 即可,允许用户从命令行来安装、删除、管理或配置服务,并能启动和停止服务

> cd D:\elasticsearch-5.5.0\bin
> elasticsearch-service.bat
Usage: elasticsearch-service. bat install | remove | start | stop | manager [SERVICE ID]

成功安装为 Windows 服务后,可以看到控制台信息提示。

二、Elasticsearch 实战

这里使用 Elasticsearch 来存储文本数据,并通过 Spring Data Elasticsearch 快速实现访问 Elasticsearch 数据的能力。

1、启动 Elasticsearch

首先,要确保已经可以正确启动 Elasticsearch 服务器了。 Linux 下运行 bin/elasticsearch, Win￾dows 平台则运行 bin\elasticsearch. bat

2、修改 application.properties

相关的两项配置,如下:

# Elasticsearch 服务地址
spring.data.elasticsearch.cluster-nodes=localhost:9300

# 设置连接超时时间
spriηg data.elasticsearch.properties transport.tcp.connect timeout=l20s

其中,一个配置是为了配置 Elasticsearch 服务地址,另一个则是设置连接超时时间。

3、创建文档类

创建一个注解为 Document 的文档类 EsBlog,专门用于 Elasticsearch 中存储博客的文档。

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


@Document(indexName = "blog", type = "blog")
public class EsBlog implements Serializable {

    private static final long serialVersionUID = lL;

    @Id
    private String id;
    private String title;
    private String summary;
    private String content;


    // JPA 的规范要求无参构造函数;设为 protected 防止直接使用
    protected EsBlog(){}

    protected EsBlog(String title,String summary,String content){
        this.title = title;
        this.summary = summary;
        this.content = content;
    }

    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 String getSummary() {
        return summary;
    }

    public void setSummary(String summary) {
        this.summary = summary;
    }

    public String getContent() {
        return content;
    }

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

    @Override
    public String toString(){
        return String.format("EsBlog[id=%s,title='%s',summary='%s',content='%s']",id, title , summary , content) ;
    }
}

需要注意的是,在 Elasticsearch 中,主键 id 采用 String 类型。

4、创建资源库

es 包下定义资源库的接口 EsBlogRepository,该接口继承自 org.springframework.data.elasticsearch.repository.ElasticsearchRepository。

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface EsBlogRepository extends ElasticsearchRepository<EsBlog,String> {
    /**
     * 分页查询博客
     *
     * @param title
     * @param summary
     * @param content
     * @param pageable
     * @return
     */
    Page<EsBlog> findByTitleContainingOrSummaryContainingOrContentContaining(String title, String summary, String content, Pageable pageable);

}

5、创建资源库测试用例

在 test 目录下建立 es包,创建资源库测试用例 EsBlogRepositoryTest,如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class EsBlogRepositoryTest {

    @Autowired
    private EsBlogRepository esBlogRepository;

    @Before
    public void initRepositoryData() {

        // 清除所有数据
        esBlogRepository.deleteAll();

        // 初始化数据
        esBlogRepository.save(new EsBlog("Had I not seen the Sun",
                "I could have borne the shade",
                "But Light a newer Wilderness. My Wilderness has made."));
        esBlogRepository.save(new EsBlog("There is room in the halls of pleasure",
                "For a long and lordly train",
                "But one by one we must all file on, Through the narrow aisles of pain."));
        esBlogRepository.save(new EsBlog("When you are old",
                "When you are old and grey and full of sleep",
                "And nodding by the fire,take down this book."));
    }

    @Test
    public void testFindDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContaining() {
        Pageable pageable = PageRequest.of(0, 20);
        String title = "Sun";
        String summary = "is";
        String content = "down";

        Page<EsBlog> page = esBlogRepository.findByTitleContainingOrSummaryContainingOrContentContaining(title, summary, content, pageable);

        System.out.println("---------start 1");
        for (EsBlog blog : page) {
            System.out.println(blog.toString());
        }
        System.out.println("---------end 1");

        title = "the";
        summary = "the";
        content = "the";

        page = esBlogRepository.findByTitleContainingOrSummaryContainingOrContentContaining(title, summary, content, pageable);

        System.out.println("---------start 2");
        for (EsBlog blog : page) {
            System.out.println(blog.toString());
        }

        System.out.println("---------end 2");
    }
}

其中, Pageable pageable = PageRequest.of(0, 20); 是初始化一个分页请求。

在执行测试用例之前,先在 Elasticsearch 的存储库中初始化了3首诗,作为测试用的数据;而后,执行测试,并能看到搜索的结果,并将结果从控制台进行了输出

6、创建控制器

创建控制器 BlogController 用于处理博客相关的请求,如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * Blog 控制器.
 *
 * @since
 * @author
 */

@RestController
@RequestMapping("/blogs")
public class BlogController {

    @Autowired
    private EsBlogRepository esBlogRepository;

    @GetMapping
    public List<EsBlog> list(@RequestParam(value="title",required=false,defaultValue="") String title,
                             @RequestParam(value="summary",required=false,defaultValue="") String summary,
                             @RequestParam(value="content",required=false,defaultValue="") String content,
                             @RequestParam(value="pageIndex",required=false,defaultValue="0") int pageIndex,
                             @RequestParam(value="pageSize",required=false,defaultValue="10") int pageSize) {

        // 数据在 Test 里面先初始化了,这里只管取数据
        Pageable pageable = PageRequest.of(pageIndex, pageSize);
        Page<EsBlog> page = esBlogRepository.findByTitleContainingOrSummaryContainingOrContentContaining(title, summary, content, pageable);
        return page.getContent();
    }
}

7、运行

在启动项目之前,要确保 Elasticsearch 服务器已经启动 在启动项目之后,运行 EsBlogRepositoryTest 测试用例来帮助初始化数据。

而后就能访问控制器所定义的 API ,如:http://localhost:8080/blogs?title=i&summary=love&content=you。