• 【SpringBoot学习】44、SpringBoot 集成 Elasticsearch-7.6 实战


    Hello,各位小伙伴们,最近忙于公司项目,没有太多的时间分享技术文档,今天抽空学习一下 Elasticsearch 这门搜索引擎技术。在大数据时代,不会搜索引擎确实有点说不过去,下面我们通过简单的实战,让各位小伙伴上手这个 elasticsearch 搜索引擎,能达到企业级的实战水准

    一、环境搭建

    1、相关文档、链接

    有部分地址是使用的华为镜像地址下载的,国内的速度快,https://mirrors.huaweicloud.com

    官方文档

    下载地址,找到对应的版本,直接点击下载即可,我这里统一使用的是 7.6.1

    也有最新版本,但是为了稳定长时间使用,建议还是使用同一个版本,有哪些坑,哪些潜在问题,用多了自然就熟练了,有时间再多研究新版本

    2、软件安装

    本片文章讲解 Windows 环境下的操作,Linux 环境下搭建 ES 环境,会在 Linux 系列文章中说明。下载完所有软件之后,如下所示

    (1)安装 Elasticsearch

    Elasticsearch 安装很简单,直接解压,找到 bin/elasticsearch.bat 双击启动就可以了

    日志中有这么一句话

    [2022-07-27T10:31:20,865][INFO ][o.e.h.AbstractHttpServerTransport] [DESKTOP-ITMR1G0] publish_address {127.0.0.1:9200}, bound_addresses {127.0.0.1:9200}, {[::1]:9200}
    
    • 1

    也就是说发布的地址是:http://127.0.0.1:9200,浏览器直接访问这个地址即可,如下所示

    Elasticsearch 就安装完成啦

    • 如果电脑配置不是很高,可以修改一下 ES 使用的内存,默认是 1G,修改 elasticsearch/confi/jvm.options 文件
    -Xms1g
    -Xmx1g
    
    • 1
    • 2

    (2)安装 Head

    Github 地址是:https://github.com/mobz/elasticsearch-head,通过上面的地址直接下载了之后解压,有多种运行 elasticsearch-head 的方法。

    下载依赖慢的,可以使用淘宝镜像地址,也就是 cnpm 下载,这里我就不做过多阐述

    • 使用内置服务器运行,访问地址:http://localhost:9100/
    git clone git://github.com/mobz/elasticsearch-head.git
    cd elasticsearch-head
    npm install
    npm run start
    open http://localhost:9100/
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这将启动一个在端口 9100 上运行的本地网络服务器,服务于 elasticsearch-head


    点击上面的连接,发现连接不上,打开 F12 控制台,发现疯狂报错跨域

    找到 elasticsearch-7.6.1/config/elasticsearch.yml 配置文件,在末尾添加跨域支持,然后重启 Elasticsearch

    # 支持跨域访问
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    • 1
    • 2
    • 3

    再次连接,发现状态变成绿色,说明连接成功

    (3)安装 IK 分词器

    Elasticsearch 安装插件的方式安装 IK 分词器,在 Elasticsearch 的目录下面有个叫 plugins 的,就是用来存放插件的目录,所以直接解压 IK 分词器复制过去即可

    重启 Elasticsearch 之后,观察一下日志,发现多了一个加载插件

    如果启动报错:Plugin [analysis-ik] was built for Elasticsearch version 8.2.3 but version7.6.1,只需要将 IK 插件的 plugin-descriptor.properties 配置文件的 ES 版本修改为安装版本即可

    (4)安装 Kibana

    安装 Kibana 不需要技巧,解压找到 bin 目录下的 kibana.bat,双击启动即可,浏览器访问:http://localhost:5601/

    • Kibana 设置中文,找到 kibana-7.6.1/config/kibana.yml 的,最后增加一个语言配置
    i18n.locale: "zh-CN"
    
    • 1

    汉化效果如下所示

    启动完成之后,可以观察一下 Head 插件的变化,是不是多了 Kibana 的信息

    二、SpringBoot 搭建 ES 环境

    1、SpringBoot 集成 ES

    直接干货,SpringBoot 基础不好的同学,建议先学习一下 SpringBoot,下面文章内容的分享,直接是在熟练使用 SpringBoot 的前提下进行的

    导入依赖到 SpringBoot,我这里使用的是 springboot 提供的

    		<dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-elasticsearchartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4

    顺着官方文档看一下,需要先注入一个 Bean,RestHighLevelClient 是用来执行请求命令的客户端

    package cn.tellsea.config;
    
    import org.apache.http.HttpHost;
    import org.elasticsearch.client.RestClient;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.context.annotation.Bean;
    
    /**
     * ES 配置
     *
     * @author Tellsea
     * @date 2022/7/28
     */
    @Configurable
    public class ElasticSearchConfig {
    
        @Bean
        public RestHighLevelClient restHighLevelClient() {
            RestHighLevelClient client = new RestHighLevelClient(
                    RestClient.builder(
                            new HttpHost("localhost", 9200, "http")
                    ));
            return client;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    到这里就集成完成了,SpringBoot 的自动装配,还是一样的轻松自如,导入依赖,加个配置就 OK 了

    2、概念入门思想

    集群,节点,索引,类型,文档,分片,映射

    elasticsearch 是面向文档,关系行数据库和 elasticsearch 客观的对比,一切都是 JSON


    直接转换到 Navicat 里面,一张图更好理解

    三、Elasticsearch 官方文档 API

    Java 高级 REST 客户端支持以下文档 API:单文档 API、多文档 API

    假设我们的单元测试类,已经做了如下操作,注入可客户端 Bean(restHighLevelClient),创建了一个全局常量,也就是索引名称

    @SpringBootTest
    class SpringBootElasticsearchApplicationTests {
    
        @Autowired
        private RestHighLevelClient restHighLevelClient;
    
        /**
         * 索引
         */
        public final static String ES_INDEX = "test_index";
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1、索引 API

    (1)创建索引

        /**
         * 创建索引
         */
        @Test
        public void createIndex() throws IOException {
            CreateIndexRequest createIndexRequest = new CreateIndexRequest(ES_INDEX);
            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            if (createIndexResponse.equals(ES_INDEX)) {
                System.out.println("创建索引成功");
            } else {
                System.out.println("创建索引失败");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (2)索引是否存在

        /**
         * 索引是否存在
         */
        @Test
        public void existsIndex() throws IOException {
            GetIndexRequest getIndexRequest = new GetIndexRequest(ES_INDEX);
            boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
            if (exists) {
                System.out.println("存在");
            } else {
                System.out.println("不存在");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (3)获取索引

        /**
         * 获取索引
         */
        @Test
        public void getIndex() throws IOException {
            GetIndexRequest getIndexRequest = new GetIndexRequest(ES_INDEX);
            GetIndexResponse getIndexResponse = restHighLevelClient.indices().get(getIndexRequest, RequestOptions.DEFAULT);
            System.out.println(getIndexResponse.getAliases());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (4)删除索引

        /**
         * 删除索引
         */
        @Test
        public void deleteIndex() throws IOException {
            DeleteIndexRequest deleteRequest = new DeleteIndexRequest(ES_INDEX);
            AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(deleteRequest, RequestOptions.DEFAULT);
            if (acknowledgedResponse.isAcknowledged()) {
                System.out.println("删除索引成功");
            } else {
                System.out.println("删除索引失败");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2、文档 API

    (1)添加文档

        /**
         * 添加文档
         */
        @Test
        public void addDocument() throws IOException {
            User user = new User().setUserName("张三").setAge(24);
            IndexRequest indexRequest = new IndexRequest(ES_INDEX);
            indexRequest.source(JSON.toJSONString(user), XContentType.JSON);
            IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
            System.out.println(indexResponse.toString());
            if ("CREATED".equals(indexResponse.status())) {
                System.out.println("新增文档");
            } else if ("UPDATE".equals(indexResponse.status())) {
                System.out.println("更新文档");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (2)文档是否存在

        /**
         * 文档是否存在
         */
        @Test
        public void existsDocument() throws IOException {
            GetRequest getRequest = new GetRequest(ES_INDEX, "1");
            boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
            if (exists) {
                System.out.println("存在");
            } else {
                System.out.println("不存在");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (3)获取文档

        /**
         * 获取文档
         */
        @Test
        public void getDocument() throws IOException {
            GetRequest getRequest = new GetRequest(ES_INDEX, "1");
            GetResponse documentFields = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
            System.out.println(documentFields.getSourceAsString());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (4)更新文档

        /**
         * 更新文档
         */
        @Test
        public void updateDocument() throws IOException {
            UpdateRequest updateRequest = new UpdateRequest(ES_INDEX, "1");
            updateRequest.timeout("1s");
            User user = new User().setUserName("李四").setAge(25);
            updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
            UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
            if (updateResponse.status().getStatus() == 200) {
                System.out.println("更新文档成功");
            } else {
                System.out.println("更新文档失败");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (4)删除文档

        /**
         * 删除文档
         */
        @Test
        public void deleteDocument() throws IOException {
            DeleteRequest deleteRequest = new DeleteRequest(ES_INDEX, "1");
            deleteRequest.timeout("1s");
            DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
            if (deleteResponse.status().getStatus() == 200) {
                System.out.println("删除文档成功");
            } else {
                System.out.println("删除文档失败");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3、批量 API

    (1)批量导入数据

        /**
         * 批量导入数据
         */
        @Test
        public void bulkAdd() throws IOException {
            BulkRequest bulkRequest = new BulkRequest(ES_INDEX);
            bulkRequest.timeout("10s");
            List<User> userList = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                userList.add(new User().setUserName("用户" + (i + 1)).setAge((i + 1) * 10));
            }
            for (int i = 0; i < userList.size(); i++) {
                bulkRequest.add(new IndexRequest(ES_INDEX)
                        .id(String.valueOf(i + 1))
                        .source(JSON.toJSONString(userList.get(i)), XContentType.JSON));
            }
            BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (bulkResponse.hasFailures()) {
                System.out.println("批量导入数据成功");
            } else {
                System.out.println("批量导入数据失败");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    (2)查询数据

        /**
         * 查询数据
         */
        @Test
        public void search() throws IOException {
            SearchRequest searchRequest = new SearchRequest(ES_INDEX);
    
            // 精准查询
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("userName.keyword", "Tellsea");
            searchSourceBuilder.query(termQueryBuilder);
    
            // 超时
            searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    
            // 分页
            searchSourceBuilder.from(1);
            searchSourceBuilder.size(10);
    
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    
            // 解析结果
            List<User> userList = new ArrayList<>();
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                userList.add(JSON.parseObject(hit.getSourceAsString(), User.class));
            }
    
            // 打印结果
            userList.forEach(item -> System.out.println(item));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    (3)查询数据-分页

        /**
         * 查询数据-分页
         */
        @Test
        public void searchPage() throws IOException {
            SearchRequest searchRequest = new SearchRequest(ES_INDEX);
    
            // 精准查询
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("userName.keyword", "Tellsea");
            searchSourceBuilder.query(termQueryBuilder);
    
            // 超时
            searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    
            // 解析结果
            List<User> userList = new ArrayList<>();
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                userList.add(JSON.parseObject(hit.getSourceAsString(), User.class));
            }
    
            // 打印结果
            userList.forEach(item -> System.out.println(item));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    (3)查询数据-高亮

        /**
         * 查询数据-高亮
         */
        @Test
        public void searchHighlight() throws IOException {
            SearchRequest searchRequest = new SearchRequest(ES_INDEX);
    
            // 精准查询
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("userName.keyword", "Tellsea");
            searchSourceBuilder.query(termQueryBuilder);
    
            // 超时
            searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    
            // 分页
            searchSourceBuilder.from(1);
            searchSourceBuilder.size(10);
    
            // 高亮
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("userName");
            highlightBuilder.preTags("");
            highlightBuilder.postTags("");
            searchSourceBuilder.highlighter(highlightBuilder);
    
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    
            // 解析结果
            List<User> userList = new ArrayList<>();
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                // 如果没有高亮,直接解析JSON放到list即可
                // userList.add(JSON.parseObject(hit.getSourceAsString(), User.class));
    
                // 解析高亮
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField userName = highlightFields.get("userName");
                // 原来的结果
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                if (userName != null) {
                    Text[] fragments = userName.fragments();
                    String n_userName = "";
                    for (Text text : fragments) {
                        n_userName += text;
                    }
                    sourceAsMap.put("userName", n_userName);
                }
                userList.add(JSON.parseObject(JSON.toJSONString(sourceAsMap), User.class));
            }
    
            // 打印结果
            userList.forEach(item -> System.out.println(item));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    到此 ES 的基本操作,已经学习完了!后续有空会整理更多高级的操作等

  • 相关阅读:
    React Hooks的理解
    UML基础与应用之面向对象
    Redis基础类型ZSet增删改查(带Java库源码)
    ITE IT66021FN/BX HDMI 1.4接收器/接收芯片/收发器
    做音视频开发要掌握哪些知识?
    基于WEB的学历信息征信系统设计与实现
    InsCode Stable Diffusion 美图活动一期——即刻体验!来自 CSDN 的 SD 模型
    在Python中实现一个简单的社交媒体应用
    java线程和go协程
    maya显示隐藏 动画长度
  • 原文地址:https://blog.csdn.net/qq_38762237/article/details/125993019