基于Redis的Geo实现附近商铺搜索(含源码)

news2024/11/18 20:43:09

微信公众号访问地址:基于Redis的Geo实现附近商铺搜索(含源码)

推荐文章:

    1、springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表;

    2、SpringBoot用线程池ThreadPoolTaskExecutor异步处理百万级数据;

    3、基于Redis的Geo实现附近商铺搜索(含源码)

    4、基于Redis实现关注、取关、共同关注及消息推送(含源码)

    5、SpringBoot整合多数据源,并支持动态新增与切换(详细教程)

    6、基于Redis实现点赞及排行榜功能

一、GEO常用命令及使用示范

1.1、GEO的数据结构

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

常见的命令有:

1、GEOADD:添加一个地理空间信息,包含:经度(longitude)、纬度(latitude)、值(member)。

案例:添加下面几条数据:

    北京南站(116.378248    39.865275)

    北京站(116.42803     39.903738)

    北京西站(116.322287     39.893729)

0>geoadd  g1 116.378248  39.865275 bjnz 116.42803 39.903738 bjz 116.322287 39.893729 bjxz"3"

2、GEODIST:计算指定的两个点之间的距离并返回。

案例:计算北京西站到北京站的距离

redis6.3:0>geodist g1 bjxz bjz km "9.0916"redis6.3:0>

3、GEOHASH:将指定 member 的坐标转为 hash 字符串形式并返回.

redis6.3:0>geohash g1 bjz1) "wx4g12k21s0"

4、GEOPOS:返回指定member的坐标

redis6.3:0>GEOPOS g1 bjz1) 1) "116.42802804708480835"   2) "39.90373880538094653"

5、GEORADIUS:指定圆心、半径,找到该圆内包含的所有 member,并按照与圆心之间的距离排序后返回。(6.2 以后已废弃)

6、GEOSEARCH:在指定范围内搜索 member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。(6.2 新功能)。在Redis中,GEOSEARCH命令用于在地理位置的有序集合中,根据给定的位置信息搜索符合条件的成员。

GEOSEARCH命令的语法如下:

GEOSEARCH key    [FROMMEMBER member]    [FROMLONLAT longitude latitude]    [BYRADIUS radius unit]    [WITHCOORDINATES]    [WITHDISTANCES]    [WITHHASH]    [COUNT count]    [ASC|DESC]    [STORE key]    [STOREDIST key]

参数说明:

    key:地理位置的有序集合的key名称。

    FROMMEMBER member:从指定成员开始搜索,返回符合条件的成员。

    FROMLONLAT longitude latitude:从给定的经度和纬度开始搜索,返回符合条件的成员。

    BYRADIUS radius unit:根据指定的半径和单位搜索符合条件的成员。

    WITHCOORD:返回成员的经纬度坐标

    WITHDIST:返回成员与中心位置的距离

    WITHHASH:返回成员的地理哈希值

    COUNT count:限制返回的成员数量。

    ASC|DESC:指定返回结果的排序方式,默认为升序

    STORE key:将搜索结果存储到指定的键名中。

    STOREDIST key:将搜索结果存储到指定的键名中,并返回成员与中心位置的距离。

案例:搜索天安门(116.397904 39.909005)附近10km内的所有火车站,并按照距离升序排序。

redis6.3:0>geosearch g1 fromlonlat 116.397904 39.909005 byradius 10 km withdist1) 1) "bjz"   2) "2.6361"
2) 1) "bjnz"   2) "5.1452"
3) 1) "bjxz"   2) "6.6723"

7、GEOSEARCHSTORE:与 GEOSEARCH 功能一致,不过可以把结果存储到一个指定的 key。(6.2.新功能)

二、应用案例

1、将店铺信息添加到缓存中

核心代码:

public void shopDataToRedis() {
        // 1.查询店铺信息
        List<Shop> list = query().list();
        // 2.把店铺分组,按照typeId分组,typeId一致的放到一个集合
        Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));
        // 3.分批完成写入Redis
        for (Map.Entry<Long, List<Shop>> entry : map.entrySet()) {
            // 3.1.获取类型id
            Long 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 经度 纬度 member
            for (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);
        }
    }
 

接口调用及结果:

2、根据商铺类型基于Geo实现附近商铺分页滚动查询

业务流程:

核心代码:

public 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、distance
        String 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.解析出id
        if (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.获取店铺id
            String shopIdStr = result.getContent().getName();
            ids.add(Long.valueOf(shopIdStr));
            // 4.3.获取距离
            Distance distance = result.getDistance();
            distanceMap.put(shopIdStr, distance);
        });
        // 5.根据id查询Shop
        String 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);
    }
 

接口调用及展示:

注意事项:

       SpringDataRedis 的 2.3.9 版本并不支持 Redis 6.2 提供的 GEOSEARCH 命令因此我们需要提示其版本,修改自己的 pom.xml,内容如下:

最终结果展示:

三、源码获取方式

     更多优秀文章,请关注个人微信公众号或搜索“程序猿小杨”查阅。然后回复:源码,可以获取该项目对应的源码及表结构,开箱即可使用。

说明:后面redis相关操作的功能都会放在此文件夹中,需要相关功能的,只需要获取最新的资源,替换项目即可

 

       如果大家对相关文章感兴趣,可以关注微信公众号"程序猿小杨",会持续更新优秀文章!欢迎大家 分享、收藏、点赞、在看,您的支持就是我坚持下去的最大动力!谢谢!

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

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

相关文章

34.Netty源码之Netty如何处理网络请求

highlight: arduino-light 通过前面两节源码课程的学习&#xff0c;我们知道 Netty 在服务端启动时会为创建 NioServerSocketChannel&#xff0c;当客户端新连接接入时又会创建 NioSocketChannel&#xff0c;不管是服务端还是客户端 Channel&#xff0c;在创建时都会初始化自己…

Azure文件共享

什么是Azure文件共享 Azure文件共享是一种在云中存储和访问文件的服务。它允许用户在不同的计算机、虚拟机和服务之间共享数据&#xff0c;并在应用程序中进行访问、修改和管理。 Azure文件共享可以用于各种用途&#xff0c;例如&#xff1a; 共享文件资源给多个虚拟机或服务…

P6685 可持久化动态仙人掌的直径问题

思路1&#xff1a;二分快速幂 #include<bits/stdc.h> using namespace std; #define int long long int n,m; bool check(int a,int b){int ans1;while(b){if(a>n)return false;if(b&1)ans*a;if(ans>n)return false;aa*a;b>>1;}return ans<n; } voi…

STM32CubeMx之freeRTOS定时器使用

需要修改定时器时钟 xTimerChangePeriod(tim1Handle,500,200);//发送队列等待时间 第二个参数为修改的ms xTimerStart(tim1Handle,100);//开启定时器 xTimerStop(tim1Handle,100);//关闭定时器 一定注意定时器任务优先级 要大一点 不然会使用不了

【GaussDB】 SQL 篇

建表语句 表的分类 普通的建表语句 复制表内容 只复制表结构 create table 新表名(like 源表名 including all); 如果希望注释被复制的话要指定including comments 复制索引、主键约束和唯一约束&#xff0c;那么需要指定including indexes including constraints &#xf…

AI 媒人:为什么图形神经网络比 MLP 更好?

一、说明 G拉夫神经网络&#xff08;GNN&#xff09;&#xff01;想象他们是人工智能世界的媒人&#xff0c;通过探索他们的联系&#xff0c;不知疲倦地帮助数据点找到朋友和人气。数字派对上的终极僚机。 现在&#xff0c;为什么这些GNN如此重要&#xff0c;你问&#xff1f;好…

铺设道路(c++题解)

题目背景 NOIP2018 提高组 D1T1 题目描述 春春是一名道路工程师&#xff0c;负责铺设一条长度为 n 的道路。 铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 n 块首尾相连的区域&#xff0c;一开始&#xff0c;第 i 块区域下陷的深度为 di​ 。 春春每天可以选择…

没有父母“托底”的人生,到底有多累?

提起著名的心理学家和人际关系学家戴尔卡耐基。 多数人都不会太陌生&#xff0c;他的《人性的弱点》一书更是家喻户晓的程度。 这本书里总是能用一些接近我们生活的实例&#xff0c;去说明一些与人交往的方法和结论&#xff0c;实用性非常强大。 不管是做人还是处事&#xf…

实验一 VMware 17 虚拟机下安装Ubuntu16.04

系列文章目录 文章目录 系列文章目录前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 古人云&#xff1a;“工欲善其事&#xff0c;必先利其器。” 我们在学习分布式数据库原理知识同时少不了实操&#xff0c;在做实验之前&#xff0c;把相关实验…

【图像分类】理论篇(2)经典卷积神经网络 Lenet~Resenet

目录 1、卷积运算 2、经典卷积神经网络 2.1 Lenet 网络构架 代码实现 2.2 Alexnet 网络构架 代码实现 2.3 VGG VGG16网络构架 代码实现 2.4 ResNet ResNet50网络构架 代码实现 1、卷积运算 在二维卷积运算中&#xff0c;卷积窗口从输入张量的左上角开始&#xff…

Python系统学习1-9-类三之特征

一、封装 数据角度&#xff1a;将一些基本数据类型复合成一个自定义类型。 优势&#xff1a;将数据与对数据的操作相关联。 代码可读性更高&#xff08;类是对象的模板&#xff09;。 行为角度&#xff1a;向类外提供必要的功能&#xff0c;隐藏实现的细节。 优势&#xff…

工具推荐:Chat2DB一款开源免费的多数据库客户端工具

文章首发地址 Chat2DB是一款开源免费的多数据库客户端工具&#xff0c;适用于Windows和Mac操作系统&#xff0c;可在本地安装使用&#xff0c;也可以部署到服务器端并通过Web页面进行访问。 相较于传统的数据库客户端软件如Navicat、DBeaver&#xff0c;Chat2DB具备了与AIGC…

第58步 深度学习图像识别:Transformer可视化(Pytorch)

一、写在前面 &#xff08;1&#xff09;pytorch_grad_cam库 这一期补上基于基于Transformer框架可视化的教程和代码&#xff0c;使用的是pytorch_grad_cam库&#xff0c;以Bottleneck Transformer模型为例。 &#xff08;2&#xff09;算法分类 pytorch_grad_cam库中包含的…

在变暖的北极,冰冻的河岸可能会被更快地侵蚀

冷冻水槽实验揭示了多年冻土河岸侵蚀对水温、河岸粗糙度和孔隙冰含量的敏感性。 阿拉斯加胡斯利亚社区附近科尤库克河沿岸 1.5 米高的河岸照片。河流横向侵蚀永久冻土层&#xff0c;使冻土和沉积物暴露在相对温暖的水和气温下&#xff0c;导致其解冻。这张银行暴露显示了一层棕…

python入门知识:分支结构

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 1.内容导图 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python资料、视频教程、代码、插件安装教程等我都准备好了&#xff0c;直接在文末名片自…

MsrayPlus多功能搜索引擎采集软件

MsrayPlus多功能搜索引擎采集软件 摘要&#xff1a; 本文介绍了一款多功能搜索引擎软件-MsrayPlus&#xff0c;该软件能够根据关键词从搜索引擎中检索相关数据&#xff0c;并提供搜索引擎任务、爬虫引擎任务和联系信息采集三大功能。我们将分析该软件在不同领域的应用&#xf…

【实战】十一、看板页面及任务组页面开发(二) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十四)

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…

Scratch 游戏 之 随机大地图生成教程

在很多生存 / 沙盒类游戏中&#xff0c;地图往往是随机生成的&#xff0c;例如&#xff1a;饥荒、我的世界等。那我们该如何在scratch中实现这一点呢&#xff1f; 在scratch中有两种办法可以实现——画笔和克隆体。我们这次先聊克隆体。 我们可以先将克隆体设置为方形的&#x…

快解析内网穿透便捷访问内网私有云

快解析内网穿透软件的首要优势在于其不改变企业现有IT架构的特点。传统的内网穿透解决方案常常需要对企业网络进行重构&#xff0c;这不仅增加了工作量&#xff0c;还可能带来不稳定的因素。而快解析则巧妙地绕过了这一问题&#xff0c;让您能够在保持原有网络设备和配置的前提…

【Unity每日一记】Physics.Raycast 相关_Unity中的“X光射线”

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…