• 社区系统项目复盘-6



    Elasticsearch实现全文搜索功能

    什么是Elasticsearch

    • Elasticsearch简介

      • 一个分布式的、Restful风格的搜索引擎
      • 支持对各种类型的数据的检索
      • 搜索速度快,可以提供实时的搜索服务
      • 便于水平扩展,可以处理PB级海量数据
    • Elasticsearch术语

      • 索引、类型、文档、字段 (与 mysql中的数据库、表、行、列相对应,ES7.0以后,废弃掉了类型的概念,索引对应表,文档对应行,字段对应列)
      • 集群、节点、分片、副本
    • Elasticsearch的下载安装

      下载elasticsearch安装包,解压缩。修改config/elasticsearch.yml文件中的cluster.name、path.data、path.logs。下载ik中文分词器(springboot、elasticsearch、ik 版本要对应),在plugins文件夹下创建名为ik的文件夹,将下载的中文分词器解压缩到新建的ik文件夹下。执行 ./bin/elasticsearch启动ES服务。

    Spring是怎么整合Elasticsearch的?

    第一步:引入依赖

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

    第二步:配置Elasticsearch

    • cluster-name、cluster-nodes
    # ElasticsearchProperties
    spring.data.elasticsearch.cluster-name=amelia # 这个就是es配置文件中配置的cluster.name
    spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
    
    • 1
    • 2
    • 3

    ⚠️ :elasticsearch有两个默认端口,9200是http访问,9300是tcp访问
    ⚠️:由于redis和es底层都使用了netty,所以会有netty启动冲突的问题,需要在启动类中进行一些配置,详见 CommunityApplication

    @PostConstruct
    public void init(){
    	// 解决netty启动冲突的问题
    	// Netty4Utils.setAvailableProcessors()
    	System.setProperty("es.set.netty.runtime.available.processors","false");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第三步:调用Spring提供的关于ES的API,访问ES服务器(Spring Data Elasticsearch)

    • ElasticsearchTemplate(类)

    • ElasticsearchRepository(接口)——这种方案更简单一点,优先选择这个

    • 示例:

      • 对实体类添加@Ducument注解,对实体中的属性也需要添加注解
      // 示例
      @Document(indexName = "discusspost",type = "_doc",shards = 6,replicas = 3)
      public class DiscussPost {
          @Id
          private int id;
          @Field(type = FieldType.Integer)
          private int userId;
          @Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart")
          private String title;
          @Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart")
          private String content;
          @Field(type = FieldType.Integer)
          private int type;
          @Field(type = FieldType.Integer)
          private int status;
          @Field(type = FieldType.Date)
          private Date createTime;
          @Field(type = FieldType.Integer)
          private int commentCount;
          @Field(type = FieldType.Double)
          private double score;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 定义ESRepository接口
      @Repository
      public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost,Integer> { //DiscussPost是数据的类型,Integer是主键的类型
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 使用 定义的接口 调用函数操作es服务器(对es服务器中的数据进行增删)
      // 示例
      // 添加一条数据
      discussRepository.save(discussPostMapper.selectDiscussPostById(241));
      // 添加一组数据
      discussRepository.saveAll(discussPostMapper.selectDiscussPosts(101,0,100,0));
      // 修改数据
      DiscussPost post = discussPostMapper.selectDiscussPostById(231);
      discussRepository.save(post);
      // 删除一条数据
      discussRepository.deleteById(231);
      // 删除全部数据
      discussRepository.deleteAll();
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 搜索,利用定义的接口进行搜索。首先需要构造搜索的条件(利用SearchQuery接口),然后使用接口的search方法查询结果。
      // 示例
      public void testSearchByRepository(){
              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))
                      .withHighlightFields(
                              new HighlightBuilder.Field("title").preTags("").postTags(""),
                              new HighlightBuilder.Field("content").preTags("").postTags("")
                      ).build();
              // elasticTemplate.queryForPage(searchQuery,class,SearchResultMapper)
              // 底层获取得到了高亮显示的值,但是没有返回
              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);
              }
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 搜索,利用elasticTemplate进行搜索。(首先需要构造搜索的条件,然后进行结果查询,对搜索得到的结果要做一个处理,实现高亮显示)
      // 示例
      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))
                      .withHighlightFields(
                              new HighlightBuilder.Field("title").preTags("").postTags(""),
                              new HighlightBuilder.Field("content").preTags("").postTags("")
                      ).build();
      
              Page<DiscussPost> page = elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {
                  @Override
                  public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, 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.setContent(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);
              }
          }
      
      • 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
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71

    开发社区搜索功能

    • 搜索服务

      • 发布帖子的时候,将帖子保存至Elasticsearch服务器。
      • 从Elasticsearch服务器删除帖子。
      • 从Elasticsearch服务器搜索帖子
    • 采用事件的方式来处理,发布事件

      • 发布帖子时,将帖子异步的提交到Elasticsearch服务器
      • 增加评论时,将帖子异步的提交到Elasticsearch服务器
      • 在消费组件中增加一个方法,消费帖子发布事件
    • 显示结果

      • 在控制器中处理搜索请求,在HTML上显示搜索结果
    • 实现细节:

      • 首先,要对entity中的实体类discusspost进行一些注解,在数据访问层创建一个DiscussPostRepository的接口,service层创建一个elasticsearch的服务类(ElasticsearchService),在表现层创建一个SearchController类,处理搜索功能。

      • 然后,在EventConsumer中创建一个消费发帖的事件【这个事件,主要就是用于把帖子存到Elasticsearch中】,分别在CommentController的发布评论方法和DiscussPostController的发布帖子方法中添加触发 发帖事件 的代码段。

      • 还有一点需要注意,由于redis和es底层都使用了netty,所以会有netty启动冲突的问题,需要在启动类中进行一些配置,详见 CommunityApplication

        在这里插入图片描述

  • 相关阅读:
    【技术积累】算法中的贪心算法【一】
    赛事个人团体报名分组成绩查询证书h5小程序开源版开发
    new Vue的时候到底做了什么
    小米机型安全删除内置软件列表 miui12 miui13 可删除内置
    智能质检火眼金睛,客服问题无所遁形
    民生银行信用卡中心金融科技24届春招面经
    一个springbatch的奇葩报错
    Java基础面试整理
    爬虫采集如何解决ip被限制的问题呢?
    上海交通大学计算机考研资料汇总
  • 原文地址:https://blog.csdn.net/wangws_sb/article/details/128101240