Redis用GEO实现附近的人功能

文章目录

  • ☃️概述
  • ☃️命令演示
  • ☃️API将数据库表中的数据导入到redis中去
  • ☃️实现附近功能


在这里插入图片描述

在这里插入图片描述


☃️概述

GEO就是Geolocation的简写形式,代表地理坐标。Redis在3.2版本中加入了对GEO的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。常见的命令有:

  • GEOADD:添加一个地理空间信息,包含:经度(longitude)、纬度(latitude)、值(member)
  • GEODIST:计算指定的两个点之间的距离并返回
  • GEOHASH:将指定member的坐标转为hash字符串形式并返回
  • GEOPOS:返回指定member的坐标
  • GEORADIUS:指定圆心、半径,找到该圆内包含的所有member,并按照与圆心之间的距离排序后返回。6.以后已废弃
  • GEOSEARCH:在指定范围内搜索member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。6.2.新功能
  • GEOSEARCHSTORE:与GEOSEARCH功能一致,不过可以把结果存储到一个指定的key。 6.2.新功能

☃️命令演示

  • 我们先来看看 GEOADD 命令,它用于添加地理空间信息。
    我会以一个简单的例子来演示:

    GEOADD places 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
    这个命令将在名为 places 的地理空间集合中添加两个地点,分别是 “Palermo” 和 “Catania”。它们的经纬度分别是 (13.361389, 38.115556) 和 (15.087269, 37.502669)。

  • 接下来是 GEODIST 命令,它用于计算两个地点之间的距离。我们可以这样演示:

    GEODIST places "Palermo" "Catania" km
    这个命令将计算 “Palermo” 和 “Catania” 之间的距离,并以千米为单位返回距离值。

  • 接着是 GEOHASH 命令,它将地点的坐标转换为哈希字符串形式:

    GEOHASH places "Palermo"
    这个命令会返回 “Palermo” 的坐标哈希字符串。

  • 下一个是 GEOPOS 命令,它返回指定地点的坐标:

    GEOPOS places "Palermo"
    这个命令会返回 “Palermo” 的经纬度坐标。

  • 然后是 GEORADIUS 命令,不过请注意这个命令在 Redis 6 版本后已经废弃了,我们可以使用 GEOSEARCH 来替代:

    GEOSEARCH places FROMMEMBER "Palermo" BYRADIUS 100 km SORT ASC
    这个命令会在以 “Palermo” 为圆心、100 千米为半径的范围内搜索,并按照与 “Palermo” 之间的距离升序排序。

  • 最后是 GEOSEARCHSTORE 命令,它与 GEOSEARCH 功能类似,但可以将结果存储到指定的键中:

    GEOSEARCHSTORE places_results places FROMMEMBER "Palermo" BYRADIUS 100 km SORT ASC STORE myresults
    这个命令会将搜索结果存储到名为 myresults 的键中。


☃️API将数据库表中的数据导入到redis中去

将数据库表中的数据导入到redis中去,redis中的GEO,GEO在redis中就一个menber和一个经纬度,我们把x和y轴传入到redis做的经纬度位置去,但我们不能把所有的数据都放入到menber中去,毕竟作为redis是一个内存级数据库,如果存海量数据,redis还是力不从心,所以我们在这个地方存储他的id即可。

但是这个时候还有一个问题,就是在redis中并没有存储type,所以我们无法根据type来对数据进行筛选,所以我们可以按照商户类型做分组,类型相同的商户作为同一组,以typeId为key存入同一个GEO集合中即可

@Test
void loadShopData() {// 1.查询店铺信息List<Shop> list = shopService.list();// 2.把店铺分组,按照typeId分组,typeId一致的放到一个集合Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));// 3.分批完成写入Redisfor (Map.Entry<Long, List<Shop>> entry : map.entrySet()) {// 3.1.获取类型idLong typeId = entry.getKey();String key = SHOP_GEO_KEY + typeId;// 3.2.获取同类型的店铺的集合List<Shop> value = entry.getValue();List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(value.size());// 3.3.写入redis GEOADD key 经度 纬度 memberfor (Shop shop : value) {// stringRedisTemplate.opsForGeo().add(key, new Point(shop.getX(), shop.getY()), shop.getId().toString());locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(),new Point(shop.getX(), shop.getY())));}stringRedisTemplate.opsForGeo().add(key, locations);}
}

☃️实现附近功能

SpringDataRedis的2.3.9版本并不支持Redis 6.2提供的GEOSEARCH命令,因此我们需要提示其版本,修改自己的POM

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><artifactId>spring-data-redis</artifactId><groupId>org.springframework.data</groupId></exclusion><exclusion><artifactId>lettuce-core</artifactId><groupId>io.lettuce</groupId></exclusion></exclusions>
</dependency>
<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>

实现

@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);
}
@Overridepublic Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {// 1.判断是否需要根据坐标查询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());}// 2.计算分页参数int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;int end = current * SystemConstants.DEFAULT_PAGE_SIZE;// 3.查询redis、按照距离排序、分页。结果:shopId、distanceString key = SHOP_GEO_KEY + typeId;GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo() // GEOSEARCH key BYLONLAT x y BYRADIUS 10 WITHDISTANCE.search(key,GeoReference.fromCoordinate(x, y),new Distance(5000),RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end));// 4.解析出idif (results == null) {return Result.ok(Collections.emptyList());}List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();if (list.size() <= from) {// 没有下一页了,结束return Result.ok(Collections.emptyList());}// 4.1.截取 from ~ end的部分List<Long> ids = new ArrayList<>(list.size());Map<String, Distance> distanceMap = new HashMap<>(list.size());list.stream().skip(from).forEach(result -> {// 4.2.获取店铺idString shopIdStr = result.getContent().getName();ids.add(Long.valueOf(shopIdStr));// 4.3.获取距离Distance distance = result.getDistance();distanceMap.put(shopIdStr, distance);});// 5.根据id查询ShopString idStr = StrUtil.join(",", ids);List<Shop> shops = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();for (Shop shop : shops) {shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());}// 6.返回return Result.ok(shops);}

在这里插入图片描述



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

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

相关文章

木馒头头戴式蓝牙耳机

这里写目录标题 木馒头二代头戴式蓝牙耳机清除连接记忆 木馒头二代头戴式蓝牙耳机清除连接记忆 在配对模式下&#xff0c;同时按住播放和暂停按钮4秒&#xff0c;LED闪烁紫色3次&#xff0c;即为清除成功。

HTML动态响应2-Servlet+Ajax实现HTTP前后台交互方式

作者:私语茶馆 前言 其他涉及到的参考章节: HTML动态响应1—Ajax动态处理服务端响应-CSDN博客 Web应用JSON解析—FastJson1.2.83/Tomcat/IDEA解析案例-CSDN博客 HTML拆分与共享方式——多HTML组合技术-CSDN博客 1.场景: WEb项目经常需要前后端交互数据,并动态修改HTML页…

OSError: [Errno 117] Structure needs cleaning

一 问题描述 OSError: [Errno 117] Structure needs cleaning: /tmp/pymp-wafeatri 我重新使用SSH登录也会提示这个类似问题 二 解决方法 2.1 尝试删除报错的文件 &#xff08;想直接看最终解决方法的可忽略此处&#xff09; sudo rm -rf /tmp/pymp-wafeatri 此种方法只能保证…

【linux-imx6ull-设备树点灯】

目录 1. 设备树简介1.1 编译-引用1.2 设备树文件结构1.3 设备树节点介绍1.3.1 特殊节点chosen 1.4 节点内容追加 2. 设备树常用OF操作函数2.1 节点寻找类2.2 属性提取类2.3 其它常用类 4. 设备树下LED实验4.1 实验简介4.2 添加LED设备节点4.3 获取设备节点并提取属性4.3.1 获取…

内网渗透-隧道搭建ssp隧道代理工具

内网渗透-隧道搭建&ssp隧道代理工具 目录 内网渗透-隧道搭建&ssp隧道代理工具spp隧道代理工具spp工作原理图cs上线主机spp代理通信服务端配置客户端配置CS配置设置CS生成木马的监听器配置CS监听上线的监听器生成木马 spp隧道搭建服务端配置客户端配置CS配置 内网穿透&a…

【机器学习300问】100、怎么理解卷积神经网络CNN中的池化操作?

一、什么是池化&#xff1f; 卷积神经网络&#xff08;CNN&#xff09;中的池化&#xff08;Pooling&#xff09;操作是一种下采样技术&#xff0c;其目的是减少数据的空间维度&#xff08;宽度和高度&#xff09;&#xff0c;同时保持最重要的特征并降低计算复杂度。池化操作不…

【吊打面试官系列】Java高并发篇 - 什么是乐观锁和悲观锁?

大家好&#xff0c;我是锋哥。今天分享关于 【什么是乐观锁和悲观锁?】面试题&#xff0c;希望对大家有帮助&#xff1b; 什么是乐观锁和悲观锁? 1、乐观锁&#xff1a; 就像它的名字一样&#xff0c;对于并发间操作产生的线程安全问题持乐观状态&#xff0c; 乐观锁认为竞争…

手拉手springboot整合kafka发送消息

环境介绍技术栈springbootmybatis-plusmysqlrocketmq软件版本mysql8IDEAIntelliJ IDEA 2022.2.1JDK17Spring Boot3.1.7kafka2.13-3.7.0 创建topic时&#xff0c;若不指定topic的分区(Partition主题分区数)数量使&#xff0c;则默认为1个分区(partition) springboot加入依赖kafk…

【深度学习基础】使用Pytorch搭建DNN深度神经网络与手写数字识别

目录 写在开头 一、DNN的搭建 问题描述与数据集 神经网络搭建 模型训练 模型评估 模型复用 二、手写数字识别 任务描述 数据集 神经网络搭建 模型训练 模型评估 写在最后 写在开头 本文将介绍如何使用PyTorch框架搭建深度神经网络模型。实现模型的搭建、模…

Ps系统教程03

选区工具的组合使用 先用魔棒将大致区域点击圈主 会发现一些零散的小区域 使用套索工具进行区域的加减&#xff08;按住shift/alt键进行相关区域加减&#xff09; 可以放大查看 基本处理完细节之后 如果把不用的填充背景直接按delete删除&#xff0c;那么原版图案就会…

【贪心算法题目练习】

1. 分发饼干 这道题目和我们之前讲到的田忌赛马的问题很相似&#xff0c;只不过这这里不需要劣等马去抵消掉优等马&#xff0c;直接上贪心策略&#xff1a; 先将两个数组排序。针对胃口较小的孩子&#xff0c;从小到大挑选饼干: i. 如果当前饼干能满足&#xff0c;直接喂(最小…

大语言模型实战——最小化模型评测

1. 引言 现在国内外的主流模型&#xff0c;在新模型发布时都会给出很多评测数据&#xff0c;用以说明当前模型在不同数据集上的测评表现&#xff08;如下面llama3发布的评测数据&#xff09;。 这些评测数据是如何给出来的呢&#xff1f;这篇文章会用一个最小化的流程来还原下…

【限免】短时傅里叶变换时频分析【附MATLAB代码】

来源&#xff1a;微信公众号&#xff1a;EW Frontier 简介 一种能够同时对信号时域和频域分析的方法——短时傅里叶变换&#xff08;STFT&#xff09;&#xff0c;可以在时频二维角度准确地描述信号 的时间、频域的局部特性&#xff0c;与其他算法不同&#xff0c;通过该算法可…

Open3D(C++) OTSU点云二值化

目录 一、算法原理二、代码实现三、结果展示1、原始点云2、二值化本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 最大类间方差法(Between-class scatter method)是一种用于分割的方法,它通过计算图…

【C++】命名空间

命名空间 为了解决C语言命名冲突问题而诞生 namespace 命名空间名 {...... }命名空间内函数作用域只在此命名空间内 错误 using std::cout; //为了保证正常输出先忽略此行 using std::endl; //为了保证正常输出先忽略此行 #include <iostream>namespace a {int n10…

git 代码提交规范,feat,fix,chore都是什么意思?

写到前面 经常看到别人提交的代码记录里面包含一些feat、fix、chore等等&#xff0c;而我在提交时也不会区分什么&#xff0c;直接写下提交信息&#xff0c;今天就来看一下怎么个事&#xff0c;就拿 element-plus 举例来看一下 其实这么写是一种代码提交规范&#xff0c;当然…

SpringBoot六种API请求参数读取方式

SpringBoot六种API请求参数读取方式 同步请求和异步请求 同步: 指单线程依次做几件事异步: 指多线程同时做几件事 同步请求: 指客户端浏览器只有一个主线程, 此线程负责页面的渲染和发出请求等操作, 如果此主线程发出请求的话则停止渲染而且会清空页面显示的内容 直到服务器响…

前端html-docx实现html转word,并导出文件,文字+图片

前端html-docx实现html转word&#xff0c;并导出文件 前端web页面 有文字&#xff0c;有图片&#xff0c;保存web的css效果 使用工具&#xff1a;html-docx 官方网址&#xff1a;http://docs.asprain.cn/html-docx/readme.html 步骤&#xff1a; 1 npm install html-docx-js…

输入3个字符串,要求将字母按由小到大顺序输出

对于将3个整数按由小到大顺序输出&#xff0c;是很容易处理的。可以按照同样的算法来处理将3个字符串按大小顺序输出。可以直接写出程序。 编写程序&#xff1a; 运行结果&#xff1a; 这个程序是很好理解的。在程序中对字符串变量用关系运算符进行比较&#xff0c;如同对数值…

GUI 01:GUI 编程概述,AWT 相关知识,Frame 窗口,Panel 面板,及监听事件的应用

一、前言 记录时间 [2024-05-30] 疑问导航 GUI 是什么&#xff1f;GUI 如何使用&#xff1f;GUI 有哪些应用&#xff1f; 学习目的 写一些自己心中的小工具&#xff1b;Swing 界面的维护&#xff1b;了解 MVC 架构&#xff0c;以及监听事件。 本文对图形用户界面&#xff08…