黑马点评-附近商户实现

news2025/3/1 8:08:56

GEO数据结构

Redis在3.2版本中加入了对GEO的支持,允许存储地理坐标信息,根据经纬度来检索数据。

GEO本质上是基于sortedSet实现的,在Sorted Set中,每个成员都是与一个分数(score)相关联的,这个分数用于对成员进行排序。然而,在GEO数据结构中,Redis内部使用一个叫geohash的算法将每个地理位置的经纬度转换为一个64位的整数,这个整数在Sorted Set中被用作分数(score)。用户在使用GEO相关命令进行操作时,通常不需要直接处理这个分数。

以下为应用场景:

  1. 附近的人/地点查询: GEO数据结构可以用来实现“附近的人”或“附近的地点”等功能。比如,社交应用可以利用它来查找附近的用户,餐饮服务应用可以用它来显示用户附近的餐馆或咖啡厅。

  2. 地理位置距离计算: 可以使用GEO数据结构来计算两个地理位置之间的距离,支持不同的单位(米、千米、英里和英尺)。这对于需要展示用户和某个地点之间距离的应用非常有用。

  3. 地理位置信息检索: 应用程序可以利用GEO数据结构存储地点的地理位置信息,并快速检索。例如,旅游应用可以存储景点的位置信息,用户可以查询特定区域内的所有景点。

  4. 基于位置的服务(LBS): GEO数据结构支持构建各种基于位置的服务,如配送服务应用可以使用它来计算配送路线和距离,从而优化配送效率和成本。

  5. 地理围栏(Geofencing): 虽然Redis的GEO数据结构本身不直接支持地理围栏功能,但可以通过与其他功能结合使用来实现。地理围栏允许应用程序在用户进入或离开特定地理区域时触发特定的动作或通知,广泛应用于安全监控、资产跟踪、个性化营销等领域。

  6. 路线规划与导航: 通过结合GEO数据结构和其他算法,可以实现简单的路线规划和导航功能,帮助用户找到从一个地点到另一个地点的最佳路径。

导入店铺数据到GEO

当我们点击美食之后,会出现一系列的商家,商家中可以按照多种排序方式,我们此时关注的是距离,这个地方就需要使用到我们的GEO,向后台传入当前app收集的地址(我们此处是写死的) ,以当前坐标作为圆心,同时绑定相同的店家类型type,以及分页信息,把这几个条件传入后台,后台查询出对应的数据再返回。

Redis中GEO内部结构:

接下来将数据库表中的数据导入到redis中去,redis中的GEO,GEO在redis中就一个menber和一个经纬度,我们把x和y轴传入到redis做的经纬度位置去,但我们不能把所有的数据都放入到menber中去,毕竟作为redis是一个内存级数据库,如果存海量数据,redis还是力不从心,所以我们在这个地方存储他的id即可。但是这个时候还有一个问题,就是在redis中并没有存储type,所以我们无法根据type来对数据进行筛选,所以我们可以按照商户类型做分组,类型相同的商户作为同一组,以typeId为key存入同一个GEO集合中即可。

void loadShopData() {
    // 1.查询店铺信息
    List<Shop> list = shopService.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);
    }
}

实现附近商户功能

接口:

@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);
}

逻辑实现:

@Override
    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);
    }

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

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

相关文章

如何利用生成式人工智能助力短视频剧本创作?

短视频已成为现代人获取娱乐和信息的一种流行方式。不同于传统的电影和电视剧&#xff0c;短视频的时长通常较短&#xff0c;内容形式多样&#xff0c;更适合快节奏的社会生活。本文将讨论如何编写短视频剧本&#xff0c;以及它与传统故事在结构和内容上的区别。 简介 短视频剧…

测试工具使用技巧01-->jmeter链接mysql

前言 在做接口或者性能测试的时候&#xff0c;有时需要jmeter连接数据库做操作&#xff0c;可以看看如下实例。操作实例 在mysql数据库中有如下数据表 在jmeter导入jdbc驱动插件&#xff08;需要的留言找我拿&#xff09; 在jmeter测试计划元件最下面&#xff0c;导入jdbc.…

Unity的PICO项目基础环境搭建笔记(调试与构建应用篇)

文章目录 前言一、为设备开启开发者模式1、开启PICO VR一体机。前往设置>通用>关于本机>软件版本号2、一直点击 软件版本号 &#xff0c;直到出现 开发者 选项3、进入 开发者模式&#xff0c;打开 USB调试&#xff0c;选择 文件传输 二、实时预览应用场景1、下载PC端的…

使用 Python 读取 NetCDF 数据

栅格通用数据格式(NetCDF)通常用于存储多维地理数据。这些数据的一些示例包括温度、降水量和风速。NetCDF 中存储的变量通常每天在大片(大陆)区域进行多次测量。由于每天进行多次测量,数据值会快速积累并且变得难以处理。当每个值还分配给一个地理位置时,数据管理会更加复…

tcp流式服务和粘包问题

目录 1.概念 2.流式服务 3.粘包问题 1.概念 套接字是一个全双工的 使用TCP协议通信的双方必须先建立连接,然后才能开始数据的读写,双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输. TCP连接是全双工的,即双方的数据读写可以通过一个连接进行,完成…

什么是数据采集与监视控制系统(SCADA)?

SCADA数据采集是一种用于监控和控制工业过程的系统。它可以实时从现场设备获得数据并将其传输到中央计算机&#xff0c;以便进行监控和控制。SCADA数据采集系统通常使用传感器、仪表和控制器收集各种类型的数据&#xff0c;例如温度、压力、流量等&#xff0c;然后将这些数据汇…

git讲本地代码提交到码云https://gitee.com/

首先需要在码云中自己账号下创建一个空的仓库 第一步 如下图 第二步 仓库名字和仓库是否私有&#xff0c;其他不用选 以上操作好了以后 回到本地&#xff0c;在本地你要上传到仓库的项目路径下&#xff0c;初始化为git 执行 git init 接着&#xff0c;把远程仓库地址复制下…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——03.差异表达基因筛选(2)

内容如下&#xff1a; 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…

STL之deque容器代码详解

1 基础概念 功能&#xff1a; 双端数组&#xff0c;可以对头端进行插入删除操作。 deque与vector区别&#xff1a; vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低。 deque相对而言&#xff0c;对头部的插入删除速度回比vector快。 vector访问…

猫头虎分享已解决Bug || 云服务中断:CloudOutage, CloudProviderError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

HashMap构造函数

HashMap() /*** Constructs an empty <tt>HashMap</tt> with the default initial capacity* (16) and the default load factor (0.75).* 这是一个默认的构造方法&#xff0c;初始化的容量大小是16&#xff0c;装填因子是0.75* a 装填因子* n 关键字个数* m 总容…

Linux随记(八)

一、crontab运行shell脚本&#xff0c;py脚本 &#xff08;注意事项&#xff09; 情景描述&#xff1a; 目前有个sh脚本他最初大致内容是。 cat t11.sh#!/bin/bash source /etc/profile /bin/python3 /tmp/1.py sh /tmp/1.sh echo -e "$(date %F)" >…

C语言指针、数组学习记录

指针 指针是什么 数据在内存中存放的方式 声明一个变量int i 3;&#xff0c;那么在内存中就会分配一个大小为4字节&#xff08;因为int类型占4字节&#xff09;的内存空间给变量i&#xff0c;这块内存空间存放的数据就是变量i的值。 换句话说就是&#xff0c;在内存中给变…

MySQL--优化(索引)

MySQL–优化&#xff08;索引篇&#xff09; 定位慢查询SQL执行计划索引 存储引擎索引底层数据结构聚簇和非聚簇索引索引创建原则索引失效场景 SQL优化经验 索引 索引&#xff08;index&#xff09;是帮助 MySQL 高效获取数据的数据结构&#xff08;有序&#xff09;。在数据…

【记录37】VueBaiduMap 踩坑一

截图 错误 Error in callback for watcher “position.lng”: “TypeError: Cannot read properties of undefined (reading ‘setPosition’)” 解释 回调观察程序“content”时出错&#xff1a;“TypeError:无法读取未定义的属性&#xff08;读取’setContent’&#xff09;”…

一文掌握:B端系统表单页的作用、组件、设计要点,另附大量案例

Hi&#xff0c;我是贝格前端工场&#xff0c;本篇分享表单页该如何设计&#xff0c;读罢此文让你对表单页有全方位的认识&#xff0c;欢迎点赞评论转发&#xff0c;有需求请私信我们。 一、表单页是什么 表单页是指在Web应用程序中用于收集和提交用户输入数据的页面。它通常由…

基于J2EE的在线考试系统设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 相关技术 3 1.1 Java简介 3 1.2 JSP技术 3 1.3 MySQL数据库 4 1.4 B/S结构 4 1.5 本章小结 4 2 系统分析 5 2.1 需求分析 5 2.2 可行性分析 6 2.2.1 技术可行性 6 2.2.2 操作可行性 6 2.2.3 经济可行性 7 2.2.4 法律可行性 7 2.3 系统性能…

邓保康 App 技术支持

邓保康APP功能简介&#xff1a; 这是一款创新&#xff0c;稳定的商业购物型平台app&#xff0c;涵盖同城门店&#xff0c;便捷团购等业务需求。 如果使用过程中有任何疑问可以在帖子中提问&#xff0c;我们会第一时间进行答复和处理。 获得支持&#xff1a; 邮件&#xff1a…

kettle入门一 安装与基本使用

一、kettle概述 1、什么是kettle Kettle是一款开源的ETL工具&#xff0c;纯java编写&#xff0c;可以在Window、Linux、Unix上运行&#xff0c;绿色无需安装&#xff0c;数据抽取高效稳定。 2、Kettle工程存储方式 &#xff08;1&#xff09;以XML形式存储 &#xff08;2&…

Axure基础 各元件的作用及介绍

图像热区 增加按钮或者文本的点击区域&#xff0c;他是透明的&#xff0c;在预览时看不见。 动态面板 用来绘制一下带交互效果的元件&#xff0c;他是动态的&#xff0c;如轮播图&#xff0c;一个动态面板里可以有多个子面板&#xff0c;每一个子面板对应着不同的效果。 他…