Redis篇(应用案例 - 附近商户)(持续更新迭代)

news2024/11/6 7:24:45

目录

一、GEO数据结构的基本用法

二、导入店铺数据到GEO

三、实现附近商户功能


一、GEO数据结构的基本用法

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.新功能

二、导入店铺数据到GEO

具体场景说明:

当我们点击美食之后,会出现一系列的商家,商家中可以按照多种排序方式,我们此时关注的是距离,

这个地方就需要使用到我们的GEO,向后台传入当前app收集的地址(我们此处是写死的) ,

以当前坐标作为圆心,同时绑定相同的店家类型type,以及分页信息,把这几个条件传入后台,后台查询出对

应的数据再返回。

我们要做的事情是:将数据库表中的数据导入到redis中去,redis中的GEO,GEO在redis中就一个menber和

一个经纬度,我们把x和y轴传入到redis做的经纬度位置去,但我们不能把所有的数据都放入到menber中去,

毕竟作为redis是一个内存级数据库,如果存海量数据,redis还是力不从心,所以我们在这个地方存储他的id

即可。

但是这个时候还有一个问题,就是在redis中并没有存储type,所以我们无法根据type来对数据进行筛选,所

以我们可以按照商户类型做分组,类型相同的商户作为同一组,以typeId为key存入同一个GEO集合中即可

代码

HmDianPingApplicationTests

@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.分批完成写入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);
    }
}

三、实现附近商户功能

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

自己的POM

第一步:导入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>

第二步:

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

ShopServiceImpl

@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/2179973.html

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

相关文章

【高性能内存池】基本框架 + 固定长度内存池实现 1

高性能内存池 1. 基本框架2. 定长内存池的实现2.1 介绍定长内存池2.2 T* New()2.3 void Delete(T* obj) 3. 源码&#xff08;附赠测试&#xff09;4. 总结 1. 基本框架 高并发内存池主要由三个部分构成&#xff1a; 1.thread cache:用于小于256KB的内存的分配。线程缓存是每个…

opencv实战项目(三十):使用傅里叶变换进行图像边缘检测

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一&#xff0c;什么是傅立叶变换&#xff1f;二&#xff0c;图像处理中的傅立叶变换&#xff1a;三&#xff0c;傅里叶变换进行边缘检测&#xff1a; 一&#xff0c…

适合初学者的[JAVA]: 基础面试题

目录 说明 前言 String/StringBuffer/StringBuilder区别 第一点: 第二点: 总结&#xff1a; 反射机制 JVM内存结构 运行时数据区域被划分为5个主要组件&#xff1a; 方法区&#xff08;Method Area&#xff09; 堆区&#xff08;Heap Area&#xff09; 栈区&#x…

局部整体(七)利用python绘制圆形嵌套图

局部整体&#xff08;七&#xff09;利用python绘制圆形嵌套图 圆形嵌套图&#xff08; Circular Packing&#xff09;简介 将一组组圆形互相嵌套起来&#xff0c;以显示数据的层次关系&#xff0c;类似于矩形树图。数据集中每个实体都由一个圆表示&#xff0c;圆圈大小与其代…

Spring Task 调度任务

Spring Task是调度任务框架&#xff0c;通过配置&#xff0c;程序可以按照约定的时间自动执行代码逻辑&#xff0c;基于注解方式实现需要如下注解&#xff1a; Component 任务调度类交给Spring IOC容器管理EnableScheduling 启用 Spring 的定时任务&#xff08;Scheduling&…

专业学习|随机规划概观(内涵、分类以及例题分析)

一、随机规划概览 &#xff08;一&#xff09;随机规划的定义 随机规划是通过考虑随机变量的不确定性来制定优化决策的一种方法。其基本思想是在决策过程中&#xff0c;目标函数和约束条件可以包含随机因素。 &#xff08;1&#xff09;重点 随机规划的中心问题是选择参数&am…

最新版ingress-nginx-controller安装 使用host主机模式

最新版ingress-nginx-controller安装 使用host主机模式 文章目录 最新版ingress-nginx-controller安装 使用host主机模式单节点安装方式多节点高可用安装方式 官方参考链接&#xff1a; https://github.com/kubernetes/ingress-nginx/ https://kubernetes.github.io/ingress-ng…

05_中断与数码管动态显示

中断是单片机系统重点中的重点&#xff0c;因为有了中断&#xff0c;单片机就具备了快速协调多模块工作的能力&#xff0c;可以完成复杂的任务。本章将首先带领大家学习一些必要的 C 语言基础知识&#xff0c;然后讲解数码管动态显示的原理&#xff0c;并最终借助于中断系统来完…

VS code user setting 与 workspace setting 的区别

VS code user setting 与 workspace setting 的区别 引言正文引言 相信有不少开始接触 VS code 的小伙伴会有疑问,user setting 与 workspace setting 有什么区别呢?这里我们来说明一下 正文 首先,当我们使用 Ctrl + Shift + P 打开搜索输入 setting 后,可以弹出 4 个se…

SSM+Vue家教平台系统

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 spring-mybatis.xml3.5 spring-mvc.xml3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质创作…

网站建设中,https协议和http协议分别是什么,有什么区别?

HTTP&#xff08;超文本传输协议&#xff09;和 HTTPS&#xff08;安全超文本传输协议&#xff09;是互联网通信中两种非常关键的协议&#xff0c;它们在安全性、性能以及证书等方面存在区别。以下是具体分析&#xff1a; 安全性 HTTP&#xff1a;数据传输以明文形式进行&#…

宝塔搭建nextcould 30docker搭建onlyoffic8.0

宝塔搭建nextcould 宝塔搭建nextcould可以参考这两个博文 我搭建的是30版本的nextcould&#xff0c;服务组件用的是下面这些&#xff0c;步骤是一样的&#xff0c;只是版本不一样而已 nginx 1.24.0 建议选择nginx&#xff0c;apache没成功。 MySQL 8.0以上都可以 php 8.2.…

“你好BOE”重磅亮相首届上海国际光影节 打造“艺术x科技”顶级影像盛宴

黄浦江畔,北外滩胜地。作为首届上海国际光影节虹口区分会场的重点项目之一,9月29日-10月5日,BOE(京东方)年度标杆性品牌巡展IP“你好BOE”Super O SPACE影像科技展在上海北外滩滨江5米平台盛大启幕,BOE(京东方)携手上海电影、上影元、OUTPUT、新浪微博、海信、OPPO、京东等众多…

信创产品测试报告有什么作用?测试依据是什么?

一、信创产品测试报告是什么&#xff1f; 针对于某一款具体的软件产品或硬件产品进行的产品测试&#xff0c;验证其是否符合信创的要求。这一类产品&#xff0c;主要分为四类&#xff1a; 三类九款产品&#xff08;计算机终端、操作系统、数据库&#xff09;&#xff1b;通用…

【Python快速学习笔记02】基础语法学习(变量等)

目录 1.标识符与代码书写注意点 2.变量类型 1.标识符与代码书写注意点 &#xff08;1&#xff09;组成&#xff1a;字母&#xff0c;下划线&#xff0c;数字 &#xff08;2&#xff09;注意点&#xff1a;但是不能由数字开头&#xff0c;区分大小写 &#xff08;3&#xff…

AltiumDesigner脚本开发-DIP封装制作

1.点击工具栏的运行工具(蓝色向右三角图标)可以执行脚本程序&#xff1b; 2.点击菜单栏Run->Run可以执行脚本程序&#xff1b; 3.在脚本编辑器中&#xff0c;按键盘的F9键可以执行脚本程序&#xff1b; 4.通过菜单栏执行脚本程序&#xff08;需要将程序添加到菜单栏中&am…

Qt多线程操作sqlite数据库

问题 就是为了多线程操作sqlite数据库,为什么,因为数据库是耗时的操作,一条数据的插入,差不多200ms,如果是数据插入多了,界面会有明显的卡顿,因此必须,多线程操作数据库。 问题是这样的: 插入数据之后,接着更新界面;然而,插入数据是比较耗时的操作,尤其插入数据…

【无人机设计与技术】四旋翼无人机的建模

摘要 本项目的目标是通过 Simulink 建模和仿真&#xff0c;研究四旋翼无人机的建模、姿态控制、定点位置控制及航点规划功能。无人机建模包含了动力单元模型、控制效率模型和刚体模型&#xff0c;并运用这些模型实现了姿态控制和位置控制。姿态控制为无人机的平稳飞行提供基础…

Google Tag Manager - 服务器端代码植入

服务端跟踪出现的原因&#xff1b; 服务端跟踪主要有两个原因&#xff1a; 法律法规日趋严格&#xff0c;如GDPR&#xff0c;CCPA的的实施&#xff0c;对用户隐私保护越加严格&#xff0c;服务端跟踪可以让你对数据有完整的控制&#xff0c;你可以控制哪些数据可以发送给第三方…

墙绘交易平台设计:SpringBoot技术要点

3 系统分析 当用户确定开发一款程序时&#xff0c;是需要遵循下面的顺序进行工作&#xff0c;概括为&#xff1a;系统分析–>系统设计–>系统开发–>系统测试&#xff0c;无论这个过程是否有变更或者迭代&#xff0c;都是按照这样的顺序开展工作的。系统分析就是分析系…