• 14、Elasticsearch开发搜索功能


    搜索服务:

    • 将帖子保存在Elasticsearch服务器中。
    • 从Elasticsearch服务器删除帖子。
    • 从Elasticsearch服务器搜索帖子。
      发布事件:
    • 发布帖子时,将帖子异步的提交到Elasticsearch服务器。
    • 增加评论时,将帖子异步的提交到Elasticsearch服务器。
    • 在消费组件中增加一个方法,消费帖子发布事件。
      显示结果:
    • 在控制器中处理搜索请求,在Html上显示搜索结果。

    1、在实体类中添加注解

    使用Elasticsearch搜索的实体(帖子),必须要将数据放到Es服务器中才能搜索。因此要配置数据库中的表、字段与Es中的索引、类型等的对应关系。
    @id表示文档的id,文档类似数据库表中的行。
    @Field(type = FieldType.Text, analyzer = “ik_max_word”, searchAnalyzer = “ik_smart”),type参数是文档中字段的类型,FieldType.Text会进行分词并建立索引的字符类型;analyzer = “ik_max_word” 指定存储时的解析器,尽可能多的进行分词;searchAnalyzer = “ik_smart” 搜索时使用的解析器。

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Document(indexName = "discusspost",  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;
        // 帖子类型 0-普通帖子 1-置顶帖子 2-精华帖子
        @Field(type = FieldType.Integer)
        private int type;
        // 帖子状态 0-正常 1-精华 2-拉黑
        @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
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    dao

    @Repository
    public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {
    }
    
    • 1
    • 2
    • 3

    业务层Service

    获取命中的数据
    将命中的数据进行处理,包装成Discusspost放入list内
    高亮显示结果

    @Service
    public class ElasticsearchService {
    
        @Autowired
        private DiscussPostRepository discussPostRepository;
    
        @Autowired
        private ElasticsearchRestTemplate elasticsearchRestTemplate;
    
        /**
         * 插入或修改帖子,保存到Es服务器
         *
         * @param post
         */
        public void saveDiscussPost(DiscussPost post) {
            discussPostRepository.save(post);
        }
    
        /**
         * 删除帖子
         *
         * @param id
         */
        public void deleteDiscussPost(int id) {
            discussPostRepository.deleteById(id);
        }
    
        /**
         * 搜索并高分分页显示
         *
         * @param keyword
         * @param current
         * @param limit
         * @return
         */
        public Page<DiscussPost> searchDiscussionPost(String keyword, int current, int limit) {
            Pageable pageable = PageRequest.of(current, limit);
            NativeSearchQuery searchQuery =
                    new NativeSearchQueryBuilder()
                            .withQuery(QueryBuilders.multiMatchQuery(keyword, "title", "content"))
                            .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
                            .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                            .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                            .withPageable(pageable)
                            .withHighlightFields(
                                    new HighlightBuilder.Field("title")
                                            .preTags("")
                                            .postTags(""),
                                    new HighlightBuilder.Field("content")
                                            .preTags("")
                                            .postTags(""))
                            .build();
    
            SearchHits<DiscussPost> searchHits =
                    elasticsearchRestTemplate.search(searchQuery, DiscussPost.class);
            if (searchHits.getTotalHits() <= 0) {
                return null;
            }
            List<DiscussPost> list = new ArrayList<>();
            for (SearchHit<DiscussPost> hit : searchHits) {
                DiscussPost content = hit.getContent();
                DiscussPost post = new DiscussPost();
                BeanUtils.copyProperties(content, post);
                List<String> list1 = hit.getHighlightFields().get("title");
                if (list1 != null) {
                    post.setTitle(list1.get(0));
                }
                List<String> list2 = hit.getHighlightFields().get("content");
                if (list2 != null) {
                    post.setContent(list2.get(0));
                }
                list.add(post);
            }
            return new PageImpl<>(list, pageable, searchHits.getTotalHits());
        }
    
    }
    
    
    • 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
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    表现层

    在发布帖子和增加评论时,由生产者提交给消息队列,再由消费者异步处理,将帖子信息保存在搜索引擎Es服务器中。

    /**
         * 消费发帖事件
         *
         * @param record
         */
        @KafkaListener(topics = {TOPIC_PUBLISH})
        public void handlePublishMessage(ConsumerRecord record) {
            if (record == null || record.value() == null) {
                logger.error("消息内容为空!");
                return;
            }
    
            Event event = JSONObject.parseObject(record.value().toString(), Event.class);
            if (event == null) {
                logger.error("消息格式错误!");
                return;
            }
            // 搜索引擎保存帖子信息
            DiscussPost post = discussPostService.findDiscussPostById(event.getEntityId());
            elasticsearchService.saveDiscussPost(post);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    显示显示帖子Controller

    @GetMapping("/search")
        public String search(String keyword, Page page, Model model){
            // 搜索帖子
            org.springframework.data.domain.Page<DiscussPost> searchResult =
                    elasticsearchService.searchDiscussionPost(keyword, page.getCurrent()-1, page.getLimit());
            // 聚合数据
            List<Map<String, Object>> discussPosts = new ArrayList<>();
            if(searchResult != null){
                for (DiscussPost post : searchResult) {
                    Map<String, Object> map = new HashMap<>(16);
                    // 帖子
                    map.put("post",post);
                    // 作者
                    map.put("user",userService.findUserById(post.getUserId()));
                    // 点赞数量
                    map.put("likeCount",likeService.findEntityLikeCount(ENTITY_TYPE_POST,post.getId()));
                    discussPosts.add(map);
                }
            }
            model.addAttribute("discussPosts",discussPosts);
            model.addAttribute("keyword",keyword);
            page.setPath("/search?keyword=" + keyword);
            page.setRows(searchResult==null ? 0 : (int) searchResult.getTotalElements());
            return "site/search";
        }
    
    • 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

    注意

    在评论某条帖子后,由于Discusspost实体中评论数改变了,所以要重新放在Es服务器里面。
    而点赞数量存储在Redis中,所以不需要重新存放。

  • 相关阅读:
    空间滤波-反谐波平均滤波器
    5.mongodb 备份与恢复
    【云原生】Kubernetes----Rancher助力Kubernetes监控
    win10下基于anaconda的detectron2安装
    1.1 极限的概念
    apr_thread使用内存之谜
    Java(六)——常用类Math类
    【洛谷 P1996】约瑟夫问题 题解(数组+模拟+循环)
    DELL R730服务器开机报错:[XXX] usb 1-1-port4: disabled by hub (EMI?), re-enabling...
    windows 重启redis的方法
  • 原文地址:https://blog.csdn.net/nice___amusin/article/details/126072281