ElasticSerach+MongoDB:实现文章检索历史功能

news/2023/9/27 7:35:38

实现目标:

  • 展示用户的搜索记录10条,按照搜索关键词的时间倒序
  • 可以删除搜索记录
  • 保存历史记录,保存10条,多余的则删除最久的历史记录

image.png

数据库的选择:

用户的搜索记录,需要给每一个用户都保存一份,数据量较大,要求加载速度快,通常这样的数据存储到mongodb更合适,不建议直接存储到关系型数据库中。与redis对比,MongoDB是结构化数据,而redis中只有keyValue。

实现思路:

image.png

实现

保存

1. 创建实体类

```java @Data @Document("apusersearch") // mongoDB的映射注解 public class ApUserSearch implements Serializable { private static final long serialVersionUID = 1L;

private Integer userId; private String keyword; private Date createdTime; } ```

2. 保存userSearch

```java @Autowired private MongoTemplate mongoTemplate;

// keyword从dto中获取,userId从线程中获取 @Override public void insert(String keyword, Integer userId){ // 1. 查询当前用户的搜索关键词 Query query = Query.query(Criteria .where("userId").is(userId) .and("keyword").is(keyword)); ApUserSearch apUserSearch = ApUsermongoTemplate.findOne(query, ApUserSearch.class); // 2. 存在,更新创建时间 if(apUserSearch != null) { apUserSerach.setCreatedTime(new Date()); mongoTemplate.save(apUserSearch); return; } // 3. 不存在,判断当前历史总是是否超过10 apUserSearch = new ApUserSearch(); apUserSearch.setUserId(userId); apUserSearch.setKeyWord(keyword); apUserSearch.setCreatedTime(new Date()); // 重新排序 Query query1 = Query.query(Criteria .where("userId").is(userId); query1.with(Sort.by(Sort.Direction.DESC, "createdTime")); List list = mongoTemplate.find(query1, ApUserSearch.class); // 保存历史记录,保存10条,多余的则删除最久的历史记录 if(list == null || list.size() < 10){ mongoTemplate.save(apUserSearch); }else{ ApUserSearch lastInfo = apUserSearchList.get(apUserSearchList.size() - 1); // 替换 mongoTemplate.findAndReplace( Query.query(Criteria.where("id").is(lastInfo.getId()) , apUserSearch); }

} ```

3. 获取当前的用户

拦截器类,继承Ordered,GlobalFilter,其中重写filter方法,获取到了用户信息,存储到了header中,再重置请求

java @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1.获取request和response对象 ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); }

实现一个工具类AppThreadLocalUtil在utils的Service服务中,用于获取用户 ```java public class AppThreadLocalUtil {

private final static ThreadLocal<ApUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();//存入线程中
public static void setUser(ApUser apUser){WM_USER_THREAD_LOCAL.set(apUser);
}//从线程中获取
public static ApUser getUser(){return WM_USER_THREAD_LOCAL.get();
}//清理
public static void clear(){WM_USER_THREAD_LOCAL.remove();
}

} ``` 实现拦截器

```java public class AppTokenInterceptor implements HandlerInterceptor { // 把用户解析放在线程中 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String userId = request.getHeader("userId"); if(userId != null){ //存入到当前线程中 ApUser apUser = new ApUser(); apUser.setId(Integer.valueOf(userId)); AppThreadLocalUtil.setUser(apUser);

}return true;
}
// 清理线程
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {AppThreadLocalUtil.clear();
}

} ``` 在config中的WebMvcConfig中配置

java @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AppTokenInterceptor()).addPathPatterns("/**"); } }

4. 最后在ArticleSerachServiceImple中的search函数中添加

java ApUser user = AppThreadLocalUtil.getUser(); if(user != null && dto.getFromIndex() == 0){ // 首页 // 异步调用 保存搜索记录 apUserSearchService.insert(dto.getSearchWords(), user.getId()); }

查询

java List<ApUserSearch> apUserSearches = mongoTemplate.find(Query.query(Criteria.where("userId").is(user.getId())).with(Sort.by(Sort.Direction.DESC, "createdTime")), ApUserSearch.class);

删除

```java //1.检查参数 if(dto.getId() == null){ return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID); }

//2.判断是否登录 ApUser user = AppThreadLocalUtil.getUser(); if(user == null){ return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN); }

//3.删除 mongoTemplate.remove(Query.query(Criteria.where("userId").is(user.getId()).and("id").is(dto.getId())),ApUserSearch.class); return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS); ```


https://dhexx.cn/news/show-5071529.html

相关文章

vue2 项目中嵌入视频

案例&#xff1a; 代码&#xff1a; <template><div class"schematicDiagramIndex"><el-container><el-aside width"20rem"> <!-- <h4 style"font-size: 18px">视频演示</h4>--><div styl…

android去掉 原生锁屏

1. /frameworks/base/core/java/com/android/internal/widget/LockPatternUtils.java 直接 return true 2./packages/apps/Settings/src/com/android/settings/password/ScreenLockType.java 都改成 none 类型

CSS 实现祥云纹理背景

&#x1fab4; 背景 最近掘金出来一个中秋创意活动&#xff0c;我准备参加一下。作品方向选择用纯css做一个中秋贺卡&#xff0c;其中有一些中秋的元素和一些简单的动画&#xff0c;而贺卡背景的实现就是本文要讲的内容。 中秋贺卡成果图&#xff08;生成gif有点失真&#x1f6…

安防视频监控平台EasyNVR无法控制云台,该如何解决?

TSINGSEE青犀视频安防监控平台EasyNVR可支持设备通过RTSP/Onvif协议接入&#xff0c;并能对接入的视频流进行处理与多端分发&#xff0c;包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等多种格式。在智慧安防等视频监控场景中&#xff0c;EasyNVR可提供视频实时监控直播、云端…

小插曲 -- 使用Linux编写 判断程序是否在运行的小程序

编写思路 首先&#xff0c;在执行“ps -elf |grep xxx”时&#xff0c;如果xxx存在&#xff0c;通常会有两条结果&#xff0c;一个是xxx对应的PID&#xff0c;一个则是grep对应的PID&#xff0c;但是如果我希望执行命令后&#xff0c;xxx存在就只有xxx对应的PID&#xff0c;不…

Windows Server 2008安装.NET Framework 3.5

安装.NET Framework 3.5一、打开服务器管理器 在开始菜单中搜索“服务器管理器” 二、添加.NET Framework 3.5.1功能 &#xff08;一&#xff09;功能-》添加功能 &#xff08;二&#xff09;选择功能“.NET Framework 3.51” 1.点击“NET Framework 3.5.1”勾选框 2.点击“添…

多线程的学习上篇

座右铭: 天行健&#xff0c;君子以自强不息;地势坤&#xff0c;君子以厚德载物. 引入进程这个概念的目的 引入进程这个概念,最主要的目的,是为了解决“并发编程"这样的问题. 这是因为CPU进入了多核心的时代 要想进一步提高程序的执行速度,就需要充分的利用CPU 的多核资源…

【Gradle-8】Gradle插件开发指南

1、前言 Gradle插件开发在Android进阶知识中是占有一定比例的&#xff0c;特别是在性能优化领域&#xff0c;基本都会涉及&#xff0c;而且跟我们日常的编译打包也息息相关&#xff0c;加上有不少招聘要求里也明确要有Gradle插件开发经验&#xff0c;所以即使大部分人的日常开…

libevent数据结构——TAILQ_结构体

TAILQ_结构体 TAILQ_结构体在文件event2/event_struct.h和文件event2/keyvalq_struct.h中都有定义&#xff0c;并且他们的定义都是一样的&#xff0c;定义了TAILQ_ENTRY、TAILQ_HEAD结构体&#xff1a; #ifndef TAILQ_ENTRY #define EVENT_DEFINED_TQENTRY_ #define TAILQ_EN…

vue项目打包_以生产环境prod模式打包_vue-cli-service 不是内部或外部命令,也不是可运行的程序---vue工作笔记0025

打开命令行: 首先执行npm install 不执行会报错: npm run build:prod --scripts-prepend-node-pathauto 然后再这样执行就是以生产环境模式打包了.