Spring boot 论坛项目实战_06

Elasticsearch,分布式搜索引擎

目前性能最好的搜索引擎

1. Elasticsearch入门

  • Elasticsearch简介

    • 一个分布式的、Restful风格的搜索引擎

      • 分布式:多台服务器、集群式部署

      • Restful:设计风格,规定前后端交互方式

    • 支持对各种类型的数据的检索

    • 搜索速度快,可以提供 实时 的搜索服务

    • 便于水平扩展,每秒可以处理PB级海量数据

  • Elasticsearch术语【mysql中相对对象】

    • 索引【库】、类型【表】、文档{json结构}【一行数据】、字段{json中一个属性}【一列】

      • 6.0 版本后,类型逐渐被废弃,索引对应【表】,类型被固定成一个单词

      • 7.0 版本后,类型彻底被废弃

    • 集群、节点【某一台服务器】、分片【对这个索引的进一步划分、提高并发能力】、副本【对分片的备份】

  • 参考网站:

 

Elasticsearch安装

  • 务必考虑自身当前的 Spring 与 Elasticsearch 兼容问题, 选择合适的版本

  • 本次环境使用的 Elasticsearch 版本为 6.4.3

配置 ES 相关属性:

  • Config/elasticsearch.yml 文件

  • 配置集群名字:自定义,这里自定为 nowcoder

  • 配置文件存储位置、日志存放位置:

配置环境变量【Windows 平台】

  • 往Path 中添加解压后ES 的 bin目录即可

 

安装中文分词软件

  • Github 搜索: elasticsearch ik

  • 选择和你安装的 ES 对应的 ik 版本

  • 下载第一个 zip 即可

  • 文件必须解压到 ES\plugins\ik 目录下

  • 将分词 ik 直接解压到 ik 文件目录下:

  • 更新网络新词 配置:

安装 Postman【可选】

  • 一个web 模拟工具

 

常用命令访问 ES【查询健康状态、查询节点、查询索引、新增索引、删除索引】

记得先去 ES 的 bin 下 启动 elasticsearch.bat 服务

 

2. Spring 整合 Elasticsearch

  • 引入依赖

    • spring-boot-starter-data-elasticsearch

  • 配置Elasticsearch

    • cluster-name、cluster-nodes

  • Spring Data Elasticsearch

    • ElasticsearchTemplate

    • ElasticsearchRepository

 

运行时报错::

NoNodeAvailableException[None of the configured nodes are available: [{#transport#-1}{127.0.0.1}{127.0.0.1:9300}]]

  • 解决方法:

    • 修改 elasticsearch.yml

    • 修改服务器地址, 记得把 http 的端口放出来, java 运行的是 FTP 的 9300 端口

 

  • Java 实现 ES 查询方法:

    • // 搜索功能
      // withQuery: 【搜索条件】
      // withSort: 【排序条件】
      // withPageable:【分页条件】
      // withHighlightFields :【指定高亮显示】
      // build() 执行方法, 生成 SearchQuery 对象实现类
        @Test
         public void testSearchByRespository(){
             SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬","title","content"))
                    .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
                    .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                    .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                    .withPageable(PageRequest.of(0,10))//of(第几页,显示条数)
                    .withHighlightFields(
                             new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                             new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                    ).build();

             // Page: 是这个方法提供的对象,不是我们自定义的
             Page<DiscussPost> page = discussRepository.search(searchQuery);
             // 一共匹配了多少条数据
             System.out.println(page.getTotalElements());
             // 一共有多少页
             System.out.println(page.getTotalPages());
             // 当前在第几页
             System.out.println(page.getNumber());
             // 每页最多显示多少数据
             System.out.println(page.getSize());

             // 遍历数据
             for (DiscussPost post : page) {
                 System.out.println(post);
            }
        }
  • 用Template 处理高亮显示

    •  @Test
          public void testSearchByTemplate() {
              SearchQuery searchQuery = new NativeSearchQueryBuilder()
                      .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content"))
                      .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
                      .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                      .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                      .withPageable(PageRequest.of(0, 10))//of(第几页,显示条数)
                      .withHighlightFields(
                              new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                              new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                      ).build();
      ​
              Page<DiscussPost> page = elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {
                  @Override
                  public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                      // 本次查询获取的多条数据
                      SearchHits hits = response.getHits();
      ​
                      if (hits.getTotalHits() <= 0) {
                          return null;
                      }
      ​
                      List<DiscussPost> list = new ArrayList<>();
                      for (SearchHit hit : hits) {
                          DiscussPost post = new DiscussPost();
      ​
                          String id = hit.getSourceAsMap().get("id").toString();
                          post.setId(Integer.valueOf(id));
      ​
                          String userId = hit.getSourceAsMap().get("userId").toString();
                          post.setUserId(Integer.valueOf(userId));
      ​
                          String title = hit.getSourceAsMap().get("title").toString();
                          post.setTitle(title);
      ​
                          String content = hit.getSourceAsMap().get("content").toString();
                          post.setContent(content);
      ​
                          String status = hit.getSourceAsMap().get("status").toString();
                          post.setStatus(Integer.valueOf(status));
      ​
                          String createTime = hit.getSourceAsMap().get("createTime").toString();
                          post.setCreateTime(new Date(Long.valueOf(createTime)));
      ​
                          String commentCount = hit.getSourceAsMap().get("commentCount").toString();
                          post.setCommentCount(Integer.valueOf(commentCount));
      ​
                          // 处理高亮显示结果
                          HighlightField titleField = hit.getHighlightFields().get("title");
                          if (titleField != null) {
                              post.setTitle(titleField.getFragments()[0].toString());
                          }
      ​
                          HighlightField contentField = hit.getHighlightFields().get("content");
                          if (contentField != null) {
                              post.setTitle(contentField.getFragments()[0].toString());
                          }
                          list.add(post);
                      }
      ​
                      return new AggregatedPageImpl(list,pageable,
                              hits.getTotalHits(),response.getAggregations(),response.getScrollId(),hits.getMaxScore());
      ​
                  }
              });
      ​
              System.out.println(page.getTotalElements());
              System.out.println(page.getTotalPages());
              System.out.println(page.getNumber());
              System.out.println(page.getSize());
              for (DiscussPost post : page) {
                  System.out.println(post);
              }
      ​
          }
    • 可以看到,现在运行结果中,关键词是带了标签的

 

3. 开发社区搜索功能

  • 搜索服务

    • 将帖子保存至 Elasticsearch 服务器

    • 从 Elasticsearch 服务器删除帖子

    • 从 Elasticsearch 服务器搜索帖子

  • 发布事件

    • 发布帖子时候,将帖子异步的提交到 Elasticsearch 服务器

    • 增加评论时,将帖子异步的提交到 Elasticsearch 服务器

    • 在消费组件中增加一个方法,消费帖子发布事件

  • 显示结果

    • 在控制器中处理搜索请求,在 HTML 上显示搜索结果

posted @ 2020-09-22 16:41  云川望雨  阅读(322)  评论(0编辑  收藏  举报