Redis:原理速成+项目实战——Redis实战13(GEO实现附近商铺、滚动分页查询)

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:Redis:原理速成+项目实战——Redis实战12(好友关注、Feed流(关注推送)、滚动分页查询)
📚订阅专栏:Redis:原理速成+项目实战
希望文章对你们有所帮助

附近的人、附近商铺这种功能现实中很常见,很显然,这种功能需要地理坐标,Redis中刚好就有实现这类功能的数据结构——GEO。

GEO实现附近商铺

  • GEO数据结构基本用法
  • 导入店铺数据到GEO
  • 实现附近商户功能

GEO数据结构基本用法

GEO全称Geolocation,代表地理坐标,Redis允许其存储地理坐标,帮助我们根据经纬度来检索数据。

GEOADD:添加地理空间信息,包含经度、维度、值
GEODIST:计算两点距离并返回
GEOHASH:将指定member的坐标转为hash字符串形式并返回
GEOPOS:返回指定member的坐标
GEOSEARCH:在指定返回内搜索member,并按照与指定点之间的距离排序后并返回。范围可以是圆形或矩形
GEOSEARCHSTORE:与GEOSEARCH功能一致,不过可以把结果存储到一个指定的key

搜索北京天安门附近10km内的所有火车站,并按照升序排序,即可用GEO相关命令,其底层也正好是SortedSet:

GEOSEARCH g1 FROMLONLAT 经度 维度 BYRADIUS 10 km WHITDIST

其中,g1存储了北京所有火车站的经纬度,FROMLONLAT表示输入的内容是经纬度,BYRADIUS表示按照圆来搜索,WHITDIST表示带上半径。

导入店铺数据到GEO

当点击某种类型的商户的时候,就应该要发出GET请求,将商户类型,页码,经纬度都作为请求参数,并且最后根据距离位置来排序,返回List<Shop>。
因为我们要利用Redis来实现距离的计算,因此所有的商户的经纬度信息都应该要存储进去,而GEO的存储结构key-value结构,其中value是经纬度和member,这里的member我们只需要将商铺的id传进去即可。
商铺的查询是根据商铺类型来做分组的,所以要将类型相同的商铺作为同一组,将typeId为key存入GEO集合即可。

编写测试类直接运行即可:

	@Testvoid loadShopData(){//查询店铺信息List<Shop> list = shopService.list();//把店铺分组,按照typeId分组,id一致的放到一个集合Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));//分批完成导入Redisfor (Map.Entry<Long, List<Shop>> entry : map.entrySet()) {//获取类型idLong typeId = entry.getKey();String key = "shop:geo:" + typeId;//获取同类型的店铺的集合List<Shop> value = entry.getValue();//写入Redis GEOADD key 经度 维度 member
//            for (Shop shop : value) {
//                stringRedisTemplate.opsForGeo().add(key, new Point(shop.getX(), shop.getY()), shop.getId().toString());
//            }//上述方式更慢,可以直接传位置集合的迭代器List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(value.size());for (Shop shop : value){locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(),new Point(shop.getX(), shop.getY())));}stringRedisTemplate.opsForGeo().add(key, locations);}}

在这里插入图片描述

实现附近商户功能

SpringDataRedis2.3.9不支持GEOSEARCH命令,一次你我们需要提示其版本,修改POM文件,首先我们需要将下面两个依赖exclude:
在这里插入图片描述
在这里插入图片描述
接着手动添加依赖,并指定版本:

	<dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.6.2</version></dependency><dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>6.1.6.RELEASE</version></dependency>

ShopController:

	@GetMapping("/of/type")public Result queryShopByType(@RequestParam("typeId") Integer typeId,@RequestParam(value = "current", defaultValue = "1") Integer current,@RequestParam(value = "x", required = false) Double x,@RequestParam(value = "y", required = false) Double y//如果没有按照距离来排序,那么传过来的参数为空) {return shopService.queryShopByType(typeId, current, x, y);}

ShopServiceImlp:

    @Overridepublic Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {//判断是否是要根据坐标查询if(x == null || y == null){//不需要根据坐标查询,说明不是按照距离排序,直接查询数据库Page<Shop> page = query().eq("type_id", typeId).page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));return Result.ok(page.getRecords());}//计算分页参数int start = (current - 1) * DEFAULT_BATCH_SIZE;int end = current * DEFAULT_BATCH_SIZE;//查询Redis,按照距离来进行排序、分页,结果:shopId与distanceString key = SHOP_GEO_KEY + typeId;//SHOP_GEO_KEY = "shop:geo:"//GEOSEARCH key BYLONLAT x y BYRADIUS 5000 WITHDISTANCEGeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(key,GeoReference.fromCoordinate(x, y),new Distance(5000),//方圆5公里以内的店铺RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance()//WITHDISTANCE.limit(end)//查询到的结果还要满足分页的情况,但是只能指定[0,end],剩下要逻辑分页);if(results == null){return Result.ok(Collections.emptyList());}//解析出idList<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();//System.out.println(list);if(list.size() <= start){//没有下一页了,没办法执行skip,直接返回return Result.ok(Collections.emptyList());}//收集Long类型的店铺idList<Long> ids = new ArrayList<>(list.size());Map<String, Distance> distanceMap = new HashMap<>(list.size());//截取start到end的分页部分,可以用stream的skip,效率更高list.stream().skip(start).forEach(result -> {//获取店铺idString shopIdStr = result.getContent().getName();//收集起来ids.add(Long.valueOf(shopIdStr));//获取距离Distance distance = result.getDistance();distanceMap.put(shopIdStr, distance);});//System.out.println(distanceMap);//根据id查询shopString idStr = StrUtil.join(",", ids);List<Shop> shops = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();//需要将距离参数传入shops,返还到前端for (Shop shop : shops) {shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());}return Result.ok(shops);}

这个代码中,查询很容易,比较有难度的地方就是做分页的时候,除了要用limit限定最低的end的范围,还要自己手动去写逻辑分页的代码,这部分比较复杂,而且我们必须要判断list.size()是否比start小,是的话才能实现这部分的逻辑分页,否则直接返回到end的查询结果,否则会报错。

如下所示,当分页的时候就会做分页查询:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/618678.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于ssm的校园预点餐系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于ssm的校园预点餐系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Sp…

大语言模型向量数据库

大语言模型&向量数据库 LARGE LANGUAGE MODELSA. Vector Database & LLM WorkflowB. Vector Database for LLMC. Potential Applications for Vector Database on LLMD. Potential Applications for LLM on Vector DatabaseE. Retrieval-Based LLMF. Synergized Exampl…

element+vue 之图片放大器

1.安装插件 npm install vue-photo-zoom-pro2.main.js导入 // 放大镜 import VuePhotoZoomPro from vue-photo-zoom-pro Vue.use(VuePhotoZoomPro)3.页面使用 <vue-photo-zoom-pro:url"imgUrl":out-zoomer"true":scale"2"style"width:…

Leetcode202快乐数(java实现)

今天分享的题目是快乐数&#xff1a; 快乐数的定义如下&#xff1a; 快乐数&#xff08;Happy Number&#xff09;是指一个正整数&#xff0c;将其替换为各个位上数字的平方和&#xff0c;重复这个过程直到最后得到的结果为1&#xff0c;或者无限循环但不包含1。如果最终结果为…

使用ElementUI的el-tab+vxe-table表格+复选框选择

效果&#xff1a; 功能&#xff1a;首先进来是全部清空的状态的 点击左边选择不同项右边会实时发送接口获取数据填充表格 复选的内容可以保留显示&#xff0c;比如A的1勾选后切换到B再切换回来A的1仍然是勾选状态 说实话官网的setCheckboxRow方法我实现不了&#xff0c;这里…

2024年华夏银行总行社会招聘公告

信息科技部自动化测试与开发类岗  工作地点&#xff1a;北京市 学历要求&#xff1a;本科及以上 工作职责 1、持续推进自动化测试的开展&#xff0c;提升自动化测试覆盖率,包括方案设计、测试分析、测试执行和总结等。 2、负责自动化测试工具和框架搭建&#xff0c;根据…

CSAPP阅读笔记-信息的表示和处理

信息的表示和处理 包括整数、浮点数的存储格式、计算中可能存在的问题等 信息存储 大多数计算机使用8位的块&#xff0c;或者字节(byte)&#xff0c;作为最小的可寻址的内存单位&#xff0c;而不是访问内存中单独的位。机器级程序将内存视为一个非常大的字节数组&#xff0c…

fisco-bcos部署pro生产版本

我这里使用的 Ubuntu20.4系统&#xff0c;linux系统把操作命令apt改为yum即可 升级安装包 apt-get update 安装jdk&#xff0c;我这里使用jdk17 apt -y install openjdk-17-jdk-headless 查看java版本 java -version 安装依赖 apt-get install -y curl docker.io docker-com…

【Databend】行列转化:一行变多行和简单分列

文章目录 数据准备和需求生成序列和分隔函数根据分隔符变多行JSON 数据简单分列总结 数据准备和需求 行列转化在实际工作中很常见&#xff0c;其中最常见的有一行变多行&#xff0c;有下面一份数据&#xff1a; drop table if exists fact_suject_data; create table if not …

基于SSM+JSP的订餐管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

金融疆界:在线支付系统渠道网关的创新设计(一)

这是《百图解码支付系统设计与实现》专栏系列文章中的第&#xff08;11.1&#xff09;篇。点击上方关注&#xff0c;深入了解支付系统的方方面面。 整个渠道网关的内容预计会分成5篇来讲&#xff1a;1&#xff09;定位、术语、概要设计。2&#xff09;领域模型、状态机设计。3…

数据结构第十三弹---链式二叉树基本操作(上)

链式二叉树 1、结构定义2、手动创建二叉树3、前序遍历4、中序遍历5、后序遍历6、层序遍历7、计算结点个数8、计算叶子结点个数9、计算第K层结点个数10、计算树的最大深度总结 1、结构定义 实现一个数据结构少不了数据的定义&#xff0c;所以第一步需要定义二叉树的机构。 typ…

学习笔记-mysql基础(DDL,DML,DQL)

一.DDL DDL,Data Definition Language,数据库定义语言,该语言包括以下内容: 对数据库的常用操作对表结构的常用操作修改表结构 1.对数据库的常用操作 -- 查看所有的数据库 show databases -- 创建数据库 create database [if not exists] test [charsetutf8] -- 切换 选择 …

记录汇川:H5U与Factory IO测试12

主程序&#xff1a; 子程序&#xff1a; IO映射 子程序&#xff1a; 辅助出料 子程序&#xff1a; 自动程序 Factory IO配置&#xff1a; 实际动作如下&#xff1a; Factory IO测试12

基于SSM的仓库在线管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

20-链表-删除链表中的节点

这是链表的第20题&#xff0c;力扣链接。 有一个单链表的 head&#xff0c;我们想删除它其中的一个节点 node。 给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。 链表的所有值都是 唯一的&#xff0c;并且保证给定的节点 node 不是链表中的最后一个节点。 删除…

支持向量机(公式推导+举例应用)

文章目录 引言间隔与支持向量机对偶问题&#xff08;拉格朗日乘子法&#xff09;SMO算法核函数软间隔与正则化软间隔正则化&#xff08;罚函数法&#xff09; 模型的稀疏性结论实验分析 引言 在机器学习领域&#xff0c;支持向量机&#xff08;Support Vector Machine&#xf…

蓝牙音视频远程控制协议(AVRCP) AV/C command格式介绍

零.声明 本专栏文章我们会以连载的方式持续更新&#xff0c;本专栏计划更新内容如下&#xff1a; 第一篇:蓝牙综合介绍 &#xff0c;主要介绍蓝牙的一些概念&#xff0c;产生背景&#xff0c;发展轨迹&#xff0c;市面蓝牙介绍&#xff0c;以及蓝牙开发板介绍。 第二篇:Trans…

生产数据不备份,用时两行泪

背景&#xff1a;项目使用pg一主一从&#xff0c;因慢sql导致查询慢&#xff0c;所以想从原本的4核加到16核&#xff0c;联系好运维后&#xff0c;打算先从从库开始操作&#xff0c;机器上的pgsql都正常关闭&#xff0c;然后停止&#xff0c;关机&#xff0c;扩容一切都很顺利&…

OpenCV-Python(34):FAST算法

目标 理解 FAST 算法的基础使用OpenCV 中的FAST 算法相关函数进行角点检测 介绍 FAST算法&#xff08;Features from Accelerated Segment Test&#xff09;是一种用于在图像中快速检测角点的算法。它是一种基于像素的检测方法&#xff0c;具有高效、准确的特点&#xff0c;常…