【Redis】GEO(地理坐标)数据结构

news2025/1/8 5:03:19

【Redis】GEO(地理坐标)数据结构

文章目录

  • 【Redis】GEO(地理坐标)数据结构
    • 1. GEO简介
      • 1.1 GEO常用命令
    • 2. 实战示例
      • 2.1 场景介绍
      • 2.2 实战
        • 2.2.1 将店铺数据导入Redis
        • 2.2.2 根据地理坐标进行分页查询

1. GEO简介

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

1.1 GEO常用命令

常见的命令有:

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

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

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

GEOPOS:返回指定member的坐标

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

GEOSEARCH:在指定范围内搜索member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。6.2.新功能

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


2. 实战示例

2.1 场景介绍

一个点评平台,有多个频道,每个频道都有很多个店铺。

我们按照频道类型做分组,相同频道的店铺作为同一组,以频道id为key存入同一个GEO集合中即可。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mVJDgbw9-1674210734685)(C:\Users\zhuhuanjie\AppData\Roaming\Typora\typora-user-images\image-20230119224510496.png)]

其中,Value值存储店铺的id。


2.2 实战

2.2.1 将店铺数据导入Redis

在项目中编写一个测试方法将店铺数据导入Redis中:

@Test
void loadShopData() {
    //1.查询店铺信息
    List<Shop> list = shopService.list();
    //2.把店铺分组,按照typeId分组,id一致的放到一个集合
    Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));
    //3.分批完成写入
    for (Map.Entry<Long, List<Shop>> entry : map.entrySet()) {
        //3.1.获取类型id
        Long typeId = entry.getKey();
        String key = "shop:geo:" + typeId;
        //3.2.获取同类型的店铺的集合
        List<Shop> value = entry.getValue();
        List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(value.size());
        //3.3.写入reids 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.2.2 根据地理坐标进行分页查询

前端传递如下参数:

  • typeId:频道id
  • current:当前页
  • x:经度
  • y:维度

后端控制器代码如下:

@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 = lambdaQuery()
                .eq(Shop::getTypeId, 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()
            .search(
                    key,
                    GeoReference.fromCoordinate(x, y),
                    new Distance(5, Metrics.KILOMETERS),
                    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());
    HashMap<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));
        Distance distance = result.getDistance();
        distanceMap.put(shopIdStr, distance);
    });
    //5.根据id查询shop
    String idStr = StrUtil.join(",", ids);
    List<Shop> shops = lambdaQuery().in(Shop::getId, ids)
            .last("ORDER BY FIELD(id," + idStr + ")")
            .list().stream().map(shop -> {
                shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
                return shop;
            }).collect(Collectors.toList());
    //6.返回
    return Result.ok(shops);
}

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

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

相关文章

BFS判重和双向广搜

目录 一、前言 二、BFS判重 1、set 判重 2、字典判重 3、跳蚱蜢&#xff08;2017年省赛&#xff0c;lanqiaoOJ题号642&#xff09; &#xff08;1&#xff09;字典去重、用 list 实现队列 &#xff08;2&#xff09;set() 去重、用 list 实现队列 &#xff08;3&#x…

setDaemon python守护进程,队列通信子线程

使用setDaemon()和守护线程这方面知识有关&#xff0c; 比如在启动线程前设置thread.setDaemon(True)&#xff0c;就是设置该线程为守护线程&#xff0c;表示该线程是不重要的,进程退出时不需要等待这个线程执行完成。这样做的意义在于&#xff1a;避免子线程无限死循环&#x…

IK分词工具

https://code.google.com/archive/p/ik-analyzer/ IK Analyzer是一个开源的&#xff0c;基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始&#xff0c; IKAnalyzer已经推出了4个大版本。最初&#xff0c;它是以开源项目Luence为应用主体的&#xff0c;结合…

ruoyi-vue版本(八)登陆页面的验证码是咋实现的

目录1 需求2 配置类3 逻辑1 需求 我们打开若依项目的登陆页面&#xff0c;看到有一个验证码功能&#xff0c;点击一下这个验证码&#xff0c;还会进行变换验证码&#xff0c;那么这个逻辑是咋实现的&#xff1b; 我们刚进这个页面&#xff0c;其实就调用了一个接口&#xff1…

整理指针相关练习

这里收录的是相关指针的练习&#xff0c;主要针对的是指针与sizeof之间的练习&#xff0c;练完你对指针的理解将更进一层喔一维数组指针练习字符数组指针练习二维数组指针练习练习总结&#xff1a;指针笔试真题一维数组指针练习 一维数组相关练习&#xff0c;下面答案是多少呢…

负载均衡的在线OJ

文章目录1.项目宏观结构(1)三个模块(2)项目宏观结构(3)编写顺序2.compile_server(1)compiler.hpp(2)runner.hpp(3)compile_run.hpp(4)compile_server.cc(5)Makefile(6)temp(7)编译运行模块总结3.comm(1)util.hpp(2)log.hpp(3)httplib.h4.基于MVC结构的OJ服务设计(oj_server)(1)…

java访问控制符/导入2023019

访问控制符&#xff08;定义的时候不加访问控制符&#xff0c;默认的就是default&#xff09;&#xff1a; 1.private&#xff08;当前类访问权限&#xff09;&#xff1a;如果类里的一个成员&#xff08;包括成员变量、方法和构造器等&#xff09;使用private访问控制符来修饰…

Java——数组中第k个最大的元素

题目链接 leetcode在线oj题——数组中第k个最大的元素 题目描述 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂…

Move_base_flex(mbf)框架理解

本文章重点在 第二部分类图解析&#xff0c;第四部分代码解析 文章目录1. move_base_flex主体代码结构树2. move_base_flex 类图解析2.1 ROS2 navigation整体架构2.2 mbf类图主体思路详解2.2.1. 抽象层&#xff08;abstract层&#xff09;2.2.2. 外部信号输入&#xff08;Actio…

【NI Multisim 14.0虚拟仪器设计——放置虚拟仪器仪表(4通道示波器)】

目录 序言 &#x1f34d;放置虚拟仪器仪表 &#x1f349;4通道示波器 1.“时基”选项组 2.“通道”选项组 序言 NI Multisim最突出的特点之一就是用户界面友好。它可以使电路设计者方便、快捷地使用虚拟元器件和仪器、仪表进行电路设计和仿真。 首先启动NI Multisim 14.0…

C语言 通讯录最终版(动态内存+实时保存)

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; 前言&#xff1a; 上期通讯录我们实现了动态内存的申请&#xff0c;但数据依然是存放在内存中&#xff0c;当程序退出的时候&#xff0c;通讯录中的数据自然就不存在了&#xff0c;等下次运行通讯录程序的时候…

什么是REST和RESTful

REST&#xff08;Representational State Transfer&#xff09;表象化状态转变&#xff08;表述性状态转变&#xff09;&#xff0c;在2000年被提出&#xff0c;基于HTTP、URI、XML、JSON等标准和协议&#xff0c;支持轻量级、跨平台、跨语言的架构设计。是Web服务的一种新的架…

单身福利专场——Python采集某相亲地数据

嗨害大家好鸭&#xff01;我是小熊猫~ 咳咳年前最后一天… 一点单身福利… 我想… 应该会有需要的吧… 环境开发: Python 3.8Pycharm 模块使用: import parselimport requestsimport csvimport re 爬虫基本思路流程: 一. 数据来源分析: 1. 明确需求: 采集数据是什么 —…

Linux基本功系列之ping命令实战

文章目录一. 命令介绍二. 语法格式及常用选项三. 参考案例3.1 测试本机与指定网站服务器之间的网络连通性3.2 指定ping的次数3.3 指定时间间隔和次数3.4 设置TTL为2553.5 极快速的测试使用大包ping四. 使用ping命令常见问题总结前言&#x1f680;&#x1f680;&#x1f680; 想…

Java项目部署到云服务器的思路

Java项目部署到云服务器的思路 1 部署项目的前提条件 1.1 购买云服务器 我购买的是腾讯云的服务器,第一年享优惠88一年 cpu好像两核的,作为入门级的也算够用了 如果第二年该续费的时候,我记得因为收到备案什么因素的影响,要提前三个月就续费了,第二年续费价格应该是510 对于学…

思科与华为设备中的OSFP配置命令以及部分实例(超详细~~!!)

目录 一、OSPF相关配置命令 1.思科设备配置命令 &#xff08;1&#xff09;启动OSPF路由进程 &#xff08;2&#xff09;激活参与OSPF路由协议的接口&#xff0c;并且通告结构属于哪个区域的OSPF &#xff08;3&#xff09;配置路由器ID &#xff08;4&#xff09;配置被动…

苹果再次舍弃3纳米,对ASML是沉重打击,ASML得靠中国救命了

苹果在昨晚发布了新款M2 Pro和M2 Max芯片&#xff0c;这两款芯片都没有采用台积电的3纳米工艺&#xff0c;其实不仅是对台积电的打击&#xff0c;也是对ASML的打击&#xff0c;意味着ASML更先进的第二代EUV光刻机可能面临着没有太大需求的问题。一、ASML的愿望ASML当前的主要利…

day43|● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

1049. 最后一块石头的重量 II 1.代码 class Solution { public:int lastStoneWeightII(vector<int>& stones) {int sum 0;for(int i: stones) {sum i;}int t sum;sum sum /2;vector<int>f(sum 1);for (int i 0; i < stones.size(); i) {for (int j …

怎样防止数据怎么泄露了

近年来&#xff0c;各种数据泄露事件越演越烈&#xff0c;数据泄密日益成为企业管理者的梦魇。数据泄密不仅给企业带来严重的直接经济损失&#xff0c;而且还在品牌价值、投资人关系、社会公众形象等多方面造成损害。因此&#xff0c;要想提升企业数据的安全性&#xff0c;就要…

容器虚拟化技术Docker(二)mysql主从配置案例、redis集群搭建及扩容、缩容案例详解

容器虚拟化技术Docker&#xff08;二&#xff09;mysql主从配置案例、redis集群搭建及扩容缩容案例详解 对docker不熟悉的可以参考&#xff1a; 容器虚拟化技术Docker&#xff08;一&#xff09;简介、安装、常见命令、数据卷、安装常规软件 1、Docker安装mysql主从复制 &am…