6.6开发社区搜索功能

2023-11-08

在这里插入图片描述

业务层:新建ElasticsearchService类

package com.nowcoder.community.service;

import com.nowcoder.community.dao.elasticsearch.DiscussPostRepository;
import com.nowcoder.community.entity.DiscussPost;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
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.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class ElasticsearchService {
//注入需要用到的bean
    @Autowired
    private DiscussPostRepository discussRepository;

    @Autowired
    private ElasticsearchTemplate elasticTemplate;//用于高亮显示

    public void saveDiscussPost(DiscussPost post) {
        discussRepository.save(post);
    }//传入帖子数据post,

    public void deleteDiscussPost(int id) {
        discussRepository.deleteById(id);
    }

    public Page<DiscussPost> searchDiscussPost(String keyword, int current, int limit) {//搜索到的结果是Page, <DiscussPost>表明里面封装了多条帖子;current表明当前是第几页。
        SearchQuery searchQuery = new NativeSearchQueryBuilder()//构造查询的对象searchQuery
                .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(PageRequest.of(current, limit))
                .withHighlightFields(
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                ).build();

        return 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());
            }
        });
    }

}

表现层:将帖子用异步的方式提交到es服务器

在CommunityConstant中定义发帖常量

/**
 * 主题: 发帖
 */
String TOPIC_PUBLISH = "publish";

在DiscussPostController中

// 触发发帖事件,把新发布的帖子存到es服务器
Event event = new Event()
        .setTopic(TOPIC_PUBLISH)//主题:发帖
        .setUserId(user.getId())//是谁触发了事件
        .setEntityType(ENTITY_TYPE_POST)
        .setEntityId(post.getId());
eventProducer.fireEvent(event);//触发事件

评论帖子以后,会修改帖子评论数量,相当于改了帖子。

发布评论:在CommentController中,添加:

// 触发评论事件
Event event = new Event()
        .setTopic(TOPIC_COMMENT)
        .setUserId(hostHolder.getUser().getId())
        .setEntityType(comment.getEntityType())
        .setEntityId(comment.getEntityId())
        .setData("postId", discussPostId);
if (comment.getEntityType() == ENTITY_TYPE_POST) {
    DiscussPost target = discussPostService.findDiscussPostById(comment.getEntityId());
    event.setEntityUserId(target.getUserId());
} else if (comment.getEntityType() == ENTITY_TYPE_COMMENT) {
    Comment target = commentService.findCommentById(comment.getEntityId());
    event.setEntityUserId(target.getUserId());
}
eventProducer.fireEvent(event);

if (comment.getEntityType() == ENTITY_TYPE_POST) {//评论可以给帖子,也可以给评论回复,只有评论给帖子的时候,才能// 触发发帖事件

    event = new Event()
            .setTopic(TOPIC_PUBLISH)
            .setUserId(comment.getUserId())
            .setEntityType(ENTITY_TYPE_POST)
            .setEntityId(discussPostId);
    eventProducer.fireEvent(event);
}

在EventConsumer中

// 消费发帖事件
@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);//存入服务器
}

新建SearchController

package com.nowcoder.community.controller;

import com.nowcoder.community.entity.DiscussPost;
import com.nowcoder.community.entity.Page;
import com.nowcoder.community.service.ElasticsearchService;
import com.nowcoder.community.service.LikeService;
import com.nowcoder.community.service.UserService;
import com.nowcoder.community.util.CommunityConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
public class SearchController implements CommunityConstant {

    @Autowired
    private ElasticsearchService elasticsearchService;//查询功能 必然要用到Elasticsearch功能

    @Autowired
    private UserService userService;//搜到帖子以后,还要展示帖子的作者。

    @Autowired
    private LikeService likeService;//展示帖子被点赞的数量

    // search?keyword=xxx
    @RequestMapping(path = "/search", method = RequestMethod.GET)//查询数据,请求方式设置为GET
    public String search(String keyword, Page page, Model model) {
        // 实现搜索帖子
        org.springframework.data.domain.Page<DiscussPost> searchResult =//搜索的结果 是个Page对象。前面这些org.springframework.data.domain.是包名,Page对象里封装的是实体类型,得到搜索结果searchResult
                elasticsearchService.searchDiscussPost(keyword, page.getCurrent() - 1, page.getLimit());//page.getLimit()指每页显示多少条数据
        // 聚合数据
        List<Map<String, Object>> discussPosts = new ArrayList<>();//最终聚合结果是一个List集合
        if (searchResult != null) {
            for (DiscussPost post : searchResult) {//遍历Result ,得到每一个帖子
                Map<String, Object> map = new HashMap<>();
                // 帖子
                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";
    }

}

最后写HML,在index首页上对搜索框进行处理

search.html

测试:

访问首页:
在这里插入图片描述
搜索:
在这里插入图片描述
搜索到:
在这里插入图片描述
只列出第一段匹配内容

换个搜索关键字:

在这里插入图片描述
登录,发布新帖子
在这里插入图片描述
在这里插入图片描述
搜索:
在这里插入图片描述
针对头像过大的问题,我们把宽和高进行设置

<img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;"><!--头像 -->

本节课利用elastic search结合之前所学的 消息队列,把搜索功能实现,

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

6.6开发社区搜索功能 的相关文章

  • Real-world Multi-object, Multi-grasp Detection论文Demo复现

    前言 本文主要参考这篇博客 然后根据自己的实际复现中遇到的问题写出 1电脑的环境 ubuntu16 04 tensorflow gpu1 12 0 cuda9 0 cudnn7 2 https github com ivalab 论文资源的
  • wsl下redis环境搭建及部署模型

    安装编译依赖 sudo apt install gcc sudo apt get install pkg config sudo apt get install build essential 遇到错误 jemalloc jemalloc
  • Java多线程-线程中断interrupt

    前言 这里主要探讨中断常用的三个方法 interrupt 在一个线程中调用需要中断现成的interrupt 方法 会对该线程发出信号 将中断状态标志为true isInterrupted 判断当前线程的中断状态 interrupted 将线
  • 从入门到入土:nmap出击:使用nmap扫描某台靶机,给出并解读靶机环境的配置情况

    此博客仅用于记录个人学习进度 学识浅薄 若有错误观点欢迎评论区指出 欢迎各位前来交流 部分材料来源网络 若有侵权 立即删除 本人博客所有文章纯属学习之用 不涉及商业利益 不合适引用 自当删除 若被用于非法行为 与我本人无关 nmap出击 任
  • 使用开源my-deploy工具实现开发环境的代码自动化部署

    编者按 由于公司内部存在的开发系统 内网开发 外网预发布 外网生产环境 程序员频繁的更新代码造成运维人员大量时间被占用 于是有了使用该开源工具的部署测试环节 在这里感谢该开源工具的作者 也希望我这边文档能多少帮助需要类似开发环境的技术人员
  • Linux之NFS服务器

    目录 Linux之NFS服务器 简介 NFS背景介绍 生产应用场景 NFS工作原理 NFS工作流程图 流程 NFS的安装 安装nfs服务 安装rpc服务 启动rpcbind服务同时设置开机自启动 启动nfs服务同时设置开机自启动 NFS的配
  • VMware中快照如何使用

    目录 自说 快照与备份的区别 使用 自说 所谓快照 简单点来讲就如同相机中的一张照片 这张照片只要存在就可以回到拍照之前的状态场景 系统快照就是把系统中的当前状态记录在一个文件中 这个文件通常在你保存虚拟机的工作空间中 当我们在使用系统是误
  • 贝叶斯网的R实现( Bayesian networks in R)bnlearn(3)

    4 参数学习 得到贝叶斯网的网络结构之后 可以对局部分布的参数进行参数估计了 这称作参数学习 4 1参数学习的基本方法 bnlearn包的参数学习函数是bn fit 其参数method给出了两种具体的方法 mle 为极大似然估计 bayes
  • 2023最新版Android studio安装入门教程(非常详细)从零基础入门到精通,看完这一篇就够了。

    目录 JDK安装与配置 一 下载JDK 二 JDK安装 三 JDK的环境配置 四 JDK的配置验证 Android studio安装 Android studio连接手机真机调试 以华为鸿蒙为例 一 新建一个android项目 二 进入项目
  • 如何将自建的matlab神经网络的激活函数使用gensim生成simulink模型

    要把自定义的matlab激活函数生成到simulink模型中 必须在simulink神经网络库的激活函数子库中添加相应的激活函数 如何打开simulink的神经网络库 这里有两种方法 一种是在命令窗口中输出 neural 就会弹出以下窗口
  • 玩转C++小项目之短链接Demo

    玩转C 小项目之短链接Demo 真实的短链接相对来说比较复杂 例如 hash算法 放号系统等等 今天只是从小项目角度模拟一个短链接实现 如何通过短短的几十行代码快速实现一个 其中涉及的几个关键点 如何将长链接缩短 如何存储映射关系 映射关系
  • 【实际开发17】- 静态测试

    静态测试技术 不运行被测试程序 对代码通过检查 阅读进行分析 目录 1 静态测试 1 静态测试三步曲 走查 审查 评审 2 编码的标准和规范 3 代码评审 1 代码走查 Walk Through 2 正式会议审查 Inspection 3
  • MySQL 主从复制(实时热备)原理与配置

    MySQL是现在普遍使用的数据库 但是如果宕机了必然会造成数据丢失 为了保证MySQL数据库的可靠性 就要会一些提高可靠性的技术 MySQL主从复制可以做到实时热备数据 本文介绍MySQL主从复制原理及其配置过程 术语 主从复制 maste
  • Android中文API最新中文版

    http www eoeandroid com thread 58597 1 1 html 转载于 https www cnblogs com lost in code archive 2013 03 13 2956940 html
  • import “cv2“ could not be resolved pylance(reportMissingImports)

    openCV系列文章目录 文章目录 openCV系列文章目录 前言 一 错误原因 二 解决方法 1 在vscode Python Select Interpreter 2 依然报错 cv2 error OpenCV 4 7 0 D a op
  • Vagrant Note

    Vagrant Note 1 vagrant 命令 vagrant init hashicorp precise64 需要先删除Vagrantfile文件 vagrant up 启动虚拟机 根据Vagrantfile文件启动 vagrant
  • 【Linux命令详解

    文章标题 简介 一 参数列表 二 使用介绍 1 基本用法 2 显示所有进程 3 显示进程详细信息 4 根据CPU使用率排序 5 查找特定进程 6 显示特定用户的进程 7 显示进程内存占用 8 查看进程树 9 实时监控进程 10 查看特定进程
  • 一行代码实现安慰剂检验

    1 什么是安慰剂检验 随着 因果推断方法 在实证研究中的使用比例不断提升 越来越多的文章也会进行安慰剂检验 其检验基本原理与医学中的安慰剂类似 即使用 假的政策发生时间或实验组 进行分析 以检验能否得到政策效应 如果依然得到了政策效应 则表

随机推荐

  • echarts tooltip显示其他字段

    这是上面的数据 let data nodes 公司名称 name 浏览器有限公司 category 0 0代表公司 1代表自然人股东 value 100 capi 持股数1200 股东列表 name 操作系统集团 category 0 va
  • -----关于Onvif链接成功但运行报错/usr/bin/ld: warning:xxx,needed by xxx,may conflict with libssl.so.1.0.0

    1 错误分析 看图 原因是openssl系统中的版本与mysql或者libevent这些库里面的openssl的版本不一致 导致链接出了问题 解决方法 重新下载与项目中mysql和libevent中openssl一样的版本 即 由于我项目中
  • Python毕业设计 机器学习新闻算法研究与实现

    文章目录 0 前言 简介 本文章博主将介绍 参与及比较算法 先说结论 实现过程 数据爬取 数据预处理 CNN文本分类 其他分类方法更新中 最后 0 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的毕设题目缺少创新和亮点 往往达
  • C/C++实现协程及原理(详细完整版)-架构师篇

    一 协程 Coroutine 简介 协程 又称微线程 纤程 英文名Coroutine 协程的概念很早就提出来了 但直到最近几年才在某些语言 如Lua 中得到广泛应用 子程序 或者称为函数 在所有语言中都是层级调用 比如A调用B B在执行过程
  • drools 7.x 加载指定的决策表

    git https github com lccbiluox2 drools test git 1 决策表 位置 Users lcc IdeaProjects drools test src main resources com drool
  • Python异步请求:处理并发任务的结果

    Python异步请求 处理并发任务的结果 处理并发任务的结果 在异步编程中 处理并发任务的结果可能会有所不同 以下是几种常见的处理方式 使用asyncio as completed asyncio as completed 函数返回一个迭代
  • 场景题之最快返回结果

    场景题之最快返回结果 问题描述 输入中文 最快从百度翻译 谷歌翻译 有道翻译获取结果返回 代码实现 思路 采用CompletableFuture实现 多个CompletableFuture可以串行执行 也可以并行执行 其中anyOf 方法只
  • 插入排序(Insertion-Sort)-- 初级排序算法

    1 插入排序 Insertion Sort 插入排序 Insertion Sort 的算法描述是一种简单直观的排序算法 它的工作原理是通过构建有序序列 对于未排序数据 在已排序序列中从后向前扫描 找到相应位置并插入 算法描述 一般来说 插入
  • doGet和doPost、Cookie和Session的原理及区别、Spring框架

    一 doGet和doPost 1 doGet GET调用用于获取服务器信息 并将其做为响应返回给客户端 当经由Web浏览器或通过HTML JSP直接访问Servlet的URL时 一般用GET调用 public class doGet ser
  • 为分类数据添加计数表(R语言实现)

    为分类数据添加计数表 R语言实现 在数据分析和统计学中 我们经常需要对分类变量进行计数 并以表格的形式展示分类的频数 R语言是一种功能强大的数据分析工具 提供了各种函数和包来处理和可视化数据 在本文中 我将向您展示如何使用R语言为划分后的分
  • Android Studio相关知识

    一 如何更新android studio 打开Help gt Check For Updates 或 打开Settings gt 搜索Updates 找到按钮Check Now 二 如何更新android studio的adb 方案一 直接
  • 【SMD & NSMD】

    正确的PCB焊盘设计对于有效地将元件焊接到电路板至关重要 对于裸焊盘组装 有两种常见的焊接方法 阻焊层定义 SMD 与非阻焊层定义 NSMD 每种方法都有自己的特点和优势 SMD Solder Mask Defined Pad 是由阻焊层来
  • Web3与智能合约交互实战

    写在前面 在最初学习以太坊的时候 很多人都是自己创建以太坊节点后 使用geth与之交互 这种使用命令行交互的方法虽然让很多程序员感到兴奋 黑客帝国的既视感 但不可能指望普通用户通过命令行使用Dapp 因此 我们需要一种友好的方式 比如一个w
  • 计算机物联网软件工程,关于计算机物联网的应用分析

    龙源期刊网 http doc 100lw com 关于计算机物联网的应用分析 作者 曹俊娜 来源 电子技术与软件工程 2016年第03期 摘要随着智能设备的不断推出和移动互联网的发展 计算机物联网技术越来越被人们所熟知 物联网技术区别于传统
  • 给网站、博客文章添加阅读次数统计,我用两行代码 搞定计数

    本文转载于不蒜子 一 安装脚本 必选 要使用统计次数必须在页面中引入busuanzi js 目前最新版如下 任何类型的个人站点使用 如果你是用的hexo 打开themes 你的主题 layout partial footer ejs添加上述
  • ! [rejected] main -> main (fetch first) error: failed to push some refs to

    rejected main gt main fetch first error failed to push some refs to 报错信息 To github com raxx xxar git rejected main gt ma
  • 快速WordPress个人博客并内网穿透发布到互联网

    快速WordPress个人博客并内网穿透发布到互联网 文章目录 快速WordPress个人博客并内网穿透发布到互联网 我们能够通过cpolar完整的搭建起一个属于自己的网站 并且通过cpolar建立的数据隧道 从而让我们存放在本地电脑上的网
  • @EnableCircuitBreaker found, but there are no implementations. Did you forget to include a starter?

    java lang IllegalStateException Annotation EnableCircuitBreaker found but there are no implementations Did you forget to
  • ChatGPT vs. Bing vs. Bard

    随着 2022 年 ChatGTP 的推出 人工智能聊天机器人的世界突然走上了一条新道路 如今 密切关注 AI 的人都知道 不同公司推出了几款产品 从谷歌拥有自己的 Bard AI 到微软发布新的 Bing AI Chat 再到 OpenA
  • 6.6开发社区搜索功能

    业务层 新建ElasticsearchService类 package com nowcoder community service import com nowcoder community dao elasticsearch Discu