Redis框架(十五):大众点评项目 共同关注方案实现?双指针筛选DB数据:Redis取交集

news/2024/11/28 15:49:45/

大众点评项目 好友关注 共同关注

  • 需求:好友关注 共同关注
  • 业务逻辑展示
    • 点击关注功能实现
    • 判断当前用户是否关注了此博主
    • 共同好友列表查询
  • 业务逻辑实现
    • 双指针筛选DB数据
    • Redis取交集
  • 总结

SpringCloud章节复习已经过去,新的章节Redis开始了,这个章节中将会回顾Redis实战项目 大众点评
主要依照以下几个原则

  1. 基础+实战的Demo和Coding上传到我的代码仓库
  2. 在原有基础上加入一些设计模式,stream+lamdba等新的糖
  3. 通过DeBug调试,进入组件源码去分析底层运行的规则和设计模式

代码会同步在我的gitee中去,觉得不错的同学记得一键三连求关注,感谢:
Redis优化-链接: RedisCommonFollowProject

需求:好友关注 共同关注

成果展示:共同关注列表查询

在这里插入图片描述

这里给出两种方案,

  1. DB查询的话,两次数据库查询IO,并且可以通过基本的stream流处理数据,进行排序等处理,通过双指针筛选,保证时间复杂度为N
  2. Redis取交集,本身就是缓存数据库,通过封装的API操作,查询

后面可以通过通过一些压测工具,JMX进行对比和梳理,先天结构上猜测,在大部分情况下,Redis应该是更快的那个

业务逻辑展示

点击关注功能实现

在这里插入图片描述

判断当前用户是否关注了此博主

在这里插入图片描述

共同好友列表查询

在这里插入图片描述

业务逻辑实现

双指针筛选DB数据

** Controller层 - 实现 **

 * @PathVariable主要用于接收http://host:port/path/{参数值}数据。* @RequestParam主要用于接收http://host:port/path?参数名=参数值数据,这里后面也可以不跟参数值。
@RestController
@RequestMapping("/blog")
public class BlogController {@Resourceprivate IBlogService blogService;@PostMappingpublic Result saveBlog(@RequestBody Blog blog) {// 获取登录用户UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());// 保存探店博文blogService.save(blog);// 返回idreturn Result.ok(blog.getId());}@PutMapping("/like/{id}")public Result likeBlog(@PathVariable("id") Long id) {// 修改点赞数量return blogService.likeBlog(id);}@GetMapping("/of/me")public Result queryMyBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {// 获取登录用户UserDTO user = UserHolder.getUser();// 根据用户查询Page<Blog> page = blogService.query().eq("user_id", user.getId()).page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据List<Blog> records = page.getRecords();return Result.ok(records);}@GetMapping("/hot")public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {return blogService.queryHotBlog(current);}@GetMapping("/{id}")public Result queryBlogById(@PathVariable("id") Long id){return blogService.queryBlogById(id);}@GetMapping("/likes/{id}")public Result likesBlog(@PathVariable("id") Long id) {// 修改点赞数量return blogService.queryBlogLikes(id);}/*** @RequestParam与@PathVariable为spring的注解,都可以用于在Controller层接收前端传递的数据,不过两者的应用场景不同。** @PathVariable主要用于接收http://host:port/path/{参数值}数据。* @RequestParam主要用于接收http://host:port/path?参数名=参数值数据,这里后面也可以不跟参数值。* @param current* @param id* @return*/@GetMapping("/of/user")public Result queryBlogByUserId(@RequestParam(value = "current", defaultValue = "1") Integer current,@RequestParam("id") Long id){return blogService.queryBlogByUserId(current, id);}}

** Service层 - 实现 **


@Slf4j
@Service
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {@Resourceprivate IUserService userService;@Overridepublic Result followJudge(Long id, Boolean isFollow) {UserDTO user = UserHolder.getUser();if(isFollow){Follow follow = new Follow();follow.setUserId(user.getId()).setFollowUserId(id);save(follow);}else{
//            removeById(new QueryWrapper<Follow>().eq("user_id", user.getId())
//                                                    .eq("follow_user_id", id));log.debug("QueryChainWrapper" + query().select("id").eq("user_id", user.getId()).eq("follow_user_id", id));log.debug("Integer count = " + query().count());remove(new QueryWrapper<Follow>().eq("user_id", user.getId()).eq("follow_user_id", id));}return Result.ok();}@Overridepublic Result isFollowJudge(Long id) {UserDTO user = UserHolder.getUser();Integer idNum = query().eq("user_id", user.getId()).eq("follow_user_id", id).count();return Result.ok(idNum > 0);}

共同好友功能实现;

  • 双指针

       判断思想:       有序 数组A{ 1 5 8 15} B{ 8 9  15  17 22}逻辑判断大小, 选取A中最小的和B对比, 1<8  5 < 8 8=8 指针A 左移动,指针B左移,15 》9,  指针B左移,直至两边指针有一个结束为止
    
/*** @Function 共同好友* @param blogHostId* @return*/@Overridepublic Result isCommonFollow(Long blogHostId) {Long userId = UserHolder.getUser().getId();if(userId==null){return Result.fail("需要登录后进行");}List<Long> userFollowList = query().eq("user_id", userId).list().stream().map(s->s.getFollowUserId()).sorted().collect(Collectors.toList());List<Long> hostFollowList = query().eq("user_id", blogHostId).list().stream().map(s->s.getFollowUserId()).sorted().collect(Collectors.toList());List<Long> commonFollowList = new ArrayList<>();/*** 没啥暴力呗,两个For,直接n*n*/
/*        userFollowList.stream().forEach(s->{for (int i = 0; i < hostFollowList.size(); i++) {//这里遍历的时间复杂度是n*n,我们可以使用双指针法,可以使时间复杂度 缩短为 nif(s.equals(hostFollowList.get(i))){commonFollowList.add(s);break;}}});*//*** 判断思想:       有序 数组A{ 1 5 8 15} B{ 8 9  15  17 22}* 逻辑判断大小, 选取A中最小的和B对比, 1<8  5 < 8 8=8 指针A 左移动,指针B左移,* 15 》9,  指针B左移,直至两边指针有一个结束为止*/int pointA = 0, pointB = 0;while(pointA<userFollowList.size() && pointB<hostFollowList.size()){if(userFollowList.get(pointA).equals(hostFollowList.get(pointB))){commonFollowList.add(userFollowList.get(pointA));pointA++;pointB++;}else if(userFollowList.get(pointA)<hostFollowList.get(pointB)){pointA++;}else{pointB++;}}List<UserDTO> userList = commonFollowList.stream().map(id -> {User user = userService.query().eq("id", id).one();return BeanUtil.copyProperties(user, UserDTO.class);}).collect(Collectors.toList());if (userList == null) {return Result.ok("没有共同关注的好友");}return Result.ok(userList);}
}

Redis取交集

存取类型:Set
通过Redis相关API:intersect

    @Overridepublic Result followJudge(Long id, Boolean isFollow) {//id:博主ID  userId:当前账号IDUserDTO user = UserHolder.getUser();if(user==null){return Result.fail("尚未登录");}String key = RedisConstants.FOLLOW_ID + user.getId();if(isFollow){Follow follow = new Follow();follow.setUserId(user.getId()).setFollowUserId(id);boolean isSave = save(follow);if (isSave) {stringRedisTemplate.opsForSet().add(key, id.toString());}}else{
//            removeById(new QueryWrapper<Follow>().eq("user_id", user.getId())
//                                                    .eq("follow_user_id", id));log.debug("QueryChainWrapper" + query().select("id").eq("user_id", user.getId()).eq("follow_user_id", id));log.debug("Integer count = " + query().count());boolean isRemove = remove(new QueryWrapper<Follow>().eq("user_id", user.getId()).eq("follow_user_id", id));if (isRemove) {stringRedisTemplate.opsForSet().remove(key, id.toString());}}return Result.ok();}

在这里插入图片描述

    /*** @Function 共同好友* @param blogHostId* @return*/@Overridepublic Result isCommonFollow(Long blogHostId) {Long userId = UserHolder.getUser().getId();String userKey = RedisConstants.FOLLOW_ID + userId;String blogHostKey = RedisConstants.FOLLOW_ID + blogHostId;Set<String> intersect = stringRedisTemplate.opsForSet().intersect(userKey, blogHostKey);if (intersect == null || intersect.isEmpty()) {return Result.ok(Collections.emptyList());}List<Long> idList = intersect.stream().map(Long::valueOf).collect(Collectors.toList());List<UserDTO> userList = userService.listByIds(idList).stream().map(user -> {return BeanUtil.copyProperties(user, UserDTO.class);}).collect(Collectors.toList());return Result.ok(userList);}

总结两个API使用:

求交集 stringRedisTemplate.opsForSet().intersect(userKey, blogHostKey);
set存数据:stringRedisTemplate.opsForSet().add(key, id.toString());
set删除数据: stringRedisTemplate.opsForSet().remove(key, id.toString()); }

MP的查询构造器:remove(new QueryWrapper<Follow>().eq("user_id", user.getId()) .eq("follow_user_id", id));

总结

在这里插入图片描述


http://www.ppmy.cn/news/4056.html

相关文章

数据结构---各类排序算法详解

Lesson6–排序 文章目录Lesson6--排序一、.排序的概念及其应用1.1排序的概念1.2常见的排序算法二、.常见排序算法的实现2.1插入排序2.1.1插入排序的基本思想&#xff1a;2.1.2直接插入排序&#xff1a;2.1.3 直接插入排序代码实现2.1.4希尔排序&#xff08;缩小增量排序&#x…

NR RedCap UE关键技术与标准化进展

【摘 要】为满足工业无线传感器、视频监控和可穿戴设备等中端物联网用例的需求,3GPP Rel-17研究定义5G新空口(NR)低复杂度低成本终端(即RedCap UE)。介绍了5G RedCap UE的主要用例与标准化目标,分析了RedCap UE降低复杂度与成本、终端节能、终端指示与识别等关键技术,分…

Mac如何做才能彻底清理垃圾

Mac磁盘空间又爆满了&#xff1f;系统运行又卡了&#xff1f;你的Mac需要清理内存缓存垃圾啦&#xff01;本文教会你如何彻底清除Mac垃圾文件&#xff0c;释放存储空间。 现在&#xff0c;Mac电脑最顶配的硬盘可达2TB。这么大的容量&#xff0c;应该够用了吧&#xff1f;可真正…

git --- revert用法

1. 什么情况下使用revert? 比如你改代码改错了,并没有发现,叫代码提交到了github,结果队友发现了,需要将你的某次提交回退,就可以使用,使用后作为一次新的提交,可以很方便的push到github,并不会有冲突的问题。 git revert 撤销某次操作,此次操作之前和之后的commit…

KEIL5软件仿真支持的器件

问题的提出 用KEIL进行软件仿真&#xff0c;想观察一下处理器STM32F091RCY的I2C和DAC引脚输出的波形&#xff0c;发现无法向波形中添加信号&#xff0c;如下图所示 当在命令行中输入 dir vtreg 指令时&#xff0c;仅仅能够显示内核的寄存器&#xff0c;外设的寄存器无法输出&a…

王卫点赞友商?北京快递保卫战,顺丰彰显大格局大气度

当下&#xff0c;随着全面放开&#xff0c;国内新冠疫情到了另一个严峻的拐点时刻&#xff0c;特别是北京&#xff0c;正日益逼近感染高峰期。 作为社会生活的毛细血管&#xff0c;快递物流企业们在当下的新冠疫情大背景下上演了一幕幕的鲜活的画面&#xff0c;快递行业市场新…

机器学习笔记:scikit-learn pipeline使用示例

0. 前言 在机器学习中&#xff0c;管道机制是指将一系列处理步骤串连起来自动地一个接一个地运行的机制。Scikit-Learn提供了pipeline类用于实现机器学习管道&#xff0c;使用起来十分方便。 既然要将不同处理步骤串联起来&#xff0c;首先必须确保每个步骤的输出与下一个步骤的…

“电池黑马”瑞浦兰钧增速惊人,动储双起飞

撰稿 | 多客 来源 | 贝多财经 12月14日&#xff0c;“电池黑马”瑞浦兰钧能源股份有限公司&#xff08;以下简称“瑞浦兰钧”&#xff09;向港交所主板提交上市申请&#xff0c;摩根士丹利和中信证券为其联席保荐人。至此&#xff0c;国内动力电池装机量排名前十的企业均已上…