GEO数据结构

目录

1. GEOADD

2. GEODIST

3. GEOHASH

3. GEOHASH

4. GEOPOS

6. GEOSEARCH

7. GEOSEARCHSTORE

应用场景

代码的逻辑分解:

比较难懂的部分:

Redis GEO 查询与分页

results 的结构:

分页处理与截取数据 

附加距离信息


1. GEOADD

功能:向指定的 key 中添加地理空间信息。

参数

  • 经度(longitude):地理位置的经度(范围:-180 到 180)。
  • 纬度(latitude):地理位置的纬度(范围:-85.05112878 到 85.05112878)。
  • 值(member):与此经纬度相关联的唯一标识符。

2. GEODIST

功能:计算两个位置之间的距离。

参数

  • 第一个 member。
  • 第二个 member。
  • 距离单位(可选):m(米)、km(千米)、mi(英里)、ft(英尺)。
  • 结果:返回两点之间的距离,单位为指定的单位。

3. GEOHASH

功能:返回指定成员的 GeoHash 值。

GeoHash 是一种将经纬度编码为字符串的算法,用于地理位置的高效存储和查询。

参数

  • 一个或多个 member。

结果:返回两点之间的距离,单位为指定的单位。

3. GEOHASH

功能:返回指定成员的 GeoHash 值。

GeoHash 是一种将经纬度编码为字符串的算法,用于地理位置的高效存储和查询。

参数

  • 一个或多个 member。

结果:返回一个字符串表示的 GeoHash 值。

4. GEOPOS

功能:返回指定成员的经纬度。

参数

  • 一个或多个 member。
结果:返回对应的经纬度数组,例如
[13.361389, 38.115556]

6. GEOSEARCH

功能:在指定的范围内搜索成员。

  • 参数
    • 中心点(可以是经纬度或某个 member)。
    • 范围(单位:mkm 等)。
    • 排序规则(ASCDESC)。
    • 可选参数:WITHDISTWITHCOORD

7. GEOSEARCHSTORE

功能:将 GEOSEARCH 的结果存储到新的 key 中。

  • 参数
    • 目标 key:存储结果的 key。
    • 源 key:原始数据的 key。
    • 查询条件:与 GEOSEARCH 相同。

应用场景

  1. 基于位置的服务(LBS):例如,寻找某地点附近的商店、餐馆或加油站。
  2. 物流管理:计算两个地址之间的距离。
  3. 社交应用:匹配同一城市或区域的用户。

注意事项

  • GEO 数据结构在存储时使用的是 Redis 的有序集合(Sorted Set),经纬度被编码为 52 位的 GeoHash,然后作为分值(score)存储。
  • 支持的查询范围有限,主要适用于地球范围内的点查询和距离计算。

代码的逻辑分解:

  1. 判断是否存在地理坐标 (xy):

    if(x == null && y == null){
        // 数据库分页查询
    }
    
  • 如果 xy 均为空,说明不需要按地理位置查询店铺,此时直接从数据库中按店铺类型(typeId)分页查询。
  • 分页参数:current 为当前页,分页大小为常量 SystemConstants.DEFAULT_PAGE_SIZE

 数据库分页查询逻辑:

Page<Shop> page = query()
    .eq("type_id", typeId)
    .page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));

处理带有地理坐标的情况: 如果提供了地理坐标,则按以下步骤处理:

  • 计算分页范围:

    int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;
    int end = current * SystemConstants.DEFAULT_PAGE_SIZE;
    
  • 根据当前页计算数据截取的起始位置(from)和结束位置(end)。

  • 从 Redis 查询 GEO 数据:

    GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(
        key,
        GeoReference.fromCoordinate(x, y), // 圆心为地理坐标x, y
        new Distance(5000), // 搜索范围5km
        RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end)
    );
    
  • 通过 Redis 的 GEO 查询:

    • 按照距离排序。
    • 限制返回的最大结果数量为 end
    • 返回结果中包含店铺 ID 和距离。
  • 检查 Redis 查询结果是否为空:

    if(results == null) return Result.ok(Collections.emptyList());
    

    截取分页内容:

    List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = results.getContent();
    if(content.size() < from) return Result.ok(Collections.emptyList());
    

    检查总结果是否足够多以满足当前分页,如果不足则返回空列表。

提取店铺 ID 和距离:

content.stream().skip(from).forEach(result -> {
    String shopId = result.getContent().getName(); // 获取店铺ID
    Distance distance = result.getDistance(); // 获取距离
    ids.add(Long.valueOf(shopId));
    distanceMap.put(shopId, distance);
});
  • 使用 skip(from) 跳过 from 之前的结果。
  • 将分页内的店铺 ID 和距离分别存入 idsdistanceMap

附加距离信息:

for (Shop shop : shops) {
    shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
}

返回查询结果:

return Result.ok(shops);

比较难懂的部分:

Redis GEO 查询与分页

GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(
    key,                                       // Redis 中存储地理位置数据的键
    GeoReference.fromCoordinate(x, y),        // 查询的圆心坐标 (经度, 纬度)
    new Distance(5000),                       // 查询的半径范围为 5000 米(5 公里)
    RedisGeoCommands.GeoSearchCommandArgs     // 额外的查询参数
        .newGeoSearchArgs()
        .includeDistance()                    // 返回结果中包含距离
        .limit(end)                           // 限制返回的最多结果数为 end
);
  • 作用: 使用 Redis 的 GEO 数据结构查询指定范围内的地理位置,并按距离排序。limit(end) 表示查询的结果最多为 end 条。
  • 这段代码的核心作用是基于地理位置的店铺查询:
    • 找到以 (x, y) 为中心,半径 5 公里的店铺。
    • 按照距离排序,最多返回 end 条记录。
    • 每条记录包含店铺 ID 和距离信息。

resultsGeoResults<RedisGeoCommands.GeoLocation<String>> 类型,表示 Redis GEO 查询的结果集。它包含了多个 GeoResult 对象,每个 GeoResult 对应一个位置的详细信息。

我们来具体说明 results 中的内容及其结构。

results 的结构:

results 的类型是 GeoResults<RedisGeoCommands.GeoLocation<String>>,它包含:

  • content:查询结果的列表,是一个 List<GeoResult<RedisGeoCommands.GeoLocation<String>>>

每个 GeoResult 包括:

  • content:具体的位置信息,类型是 RedisGeoCommands.GeoLocation<String>
    • 包含位置的唯一标识(如店铺 ID)。
  • distance:从查询圆心到该位置的距离,类型是 Distance
results
├── content: List<GeoResult<RedisGeoCommands.GeoLocation<String>>>
    ├── GeoResult 1
    │   ├── content: RedisGeoCommands.GeoLocation<String>
    │   │   ├── name: "101"   // 店铺 ID
    │   │   ├── point: Point(x=120.5, y=30.0) // 经纬度坐标
    │   ├── distance: Distance(value=1200.0, unit=METERS) // 距离
    ├── GeoResult 2
    │   ├── content: RedisGeoCommands.GeoLocation<String>
    │   │   ├── name: "102"
    │   │   ├── point: Point(x=120.6, y=30.1)
    │   ├── distance: Distance(value=1500.0, unit=METERS)
    └── ...
  • 难点:
    • Redis GEO 查询结果不直接支持分页,因此需要手动跳过一部分数据(skip(from))以实现分页效果。
分页处理与截取数据 

以下代码的主要作用是从 content 中提取分页后的店铺信息,并将店铺的 ID距离 分别存储到两个集合中,供后续使用。distanceMap 是一个 Map<String, Distance>,用于记录每个店铺的 ID 和距离。ids 是一个 ArrayList<Long>,存储所有分页后的店铺 ID。

content.stream().skip(from).forEach(result -> {
    String shopId = result.getContent().getName();
    Distance distance = result.getDistance();
    ids.add(Long.valueOf(shopId));
    distanceMap.put(shopId, distance);
});
  • 作用: 手动处理分页逻辑,跳过 from 条数据,提取目标页的数据。
  • 难点: 理解为什么需要 skip(from),因为 Redis 查询结果已经包含了 end 条数据,但需要从 from 开始取当前页。
附加距离信息
for (Shop shop : shops) {
    shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
}
  • 遍历从数据库查询出来的每个店铺。
  • 根据店铺的 ID,从 distanceMap 中找到对应的距离值。
  • 将距离值设置到当前店铺对象的 distance 属性中。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java基础常见的面试题(易错!!)

面试题一&#xff1a;为什么 Java 不支持多继承 Java 不支持多继承主要是为避免 “菱形继承问题”&#xff08;又称 “钻石问题”&#xff09;&#xff0c;即一个子类从多个父类继承到同名方法或属性时&#xff0c;编译器无法确定该调用哪个父类的成员。同时&#xff0c;多继承…

基于Python/Flask/机器学习链家网新房数据可视化及预测系统+万字文档+答辩PPT+指导搭建视频

技术栈&#xff1a; 编程语言&#xff1a;python 涉及技术&#xff1a;requests爬虫、mysql数据库、flask框架、scikit-learn机器学习预测算法、多元线性回归、Echarts可视化。 ①.需求分析&#xff1a; 1.数据爬取&#xff1a;自动化获取链家网新房数据。 2.数据存储&…

【DeepSeek-R1背后的技术】系列十一:RAG原理介绍和本地部署(DeepSeekR1+RAGFlow构建个人知识库)

【DeepSeek-R1背后的技术】系列博文&#xff1a; 第1篇&#xff1a;混合专家模型&#xff08;MoE&#xff09; 第2篇&#xff1a;大模型知识蒸馏&#xff08;Knowledge Distillation&#xff09; 第3篇&#xff1a;强化学习&#xff08;Reinforcement Learning, RL&#xff09;…

力扣LeetCode:1656 设计有序流

题目&#xff1a; 有 n 个 (id, value) 对&#xff0c;其中 id 是 1 到 n 之间的一个整数&#xff0c;value 是一个字符串。不存在 id 相同的两个 (id, value) 对。 设计一个流&#xff0c;以 任意 顺序获取 n 个 (id, value) 对&#xff0c;并在多次调用时 按 id 递增的顺序…

MATLAB在数据分析和绘图中的应用:从基础到实践

引言 股票数据分析是金融领域中的重要研究方向&#xff0c;通过对历史价格、成交量等数据的分析&#xff0c;可以帮助投资者更好地理解市场趋势和做出决策。MATLAB作为一种强大的科学计算工具&#xff0c;提供了丰富的数据处理和可视化功能&#xff0c;非常适合用于股票数据的…

2025年02月17日Github流行趋势

项目名称&#xff1a;OmniParser 项目地址url&#xff1a;https://github.com/microsoft/OmniParser 项目语言&#xff1a;Jupyter Notebook 历史star数&#xff1a;8971 今日star数&#xff1a;969 项目维护者&#xff1a;yadong-lu, ThomasDh-C, aliencaocao, nmstoker, kris…

Keepalive基础

一。简介和功能 vrrp协议的软件实现&#xff0c;原生设计目的是为了高可用ipvs服务 功能&#xff1a; 1.基于vrrp协议完成地址流动 2.为vip地址所在的节点生成ipvs规则&#xff08;在配置文件中预先定义&#xff09; 3.为ipvs集群的各RS做健康状况检测 4.基于脚本调用接口…

vue3: directive自定义指令防止重复点击

第一章 前言 相信很多小伙伴会在各个渠道上搜如何防止重复点击&#xff0c;之后会推荐什么防抖、节流来避免这一操作&#xff0c;该方法小编就不继续往下说了。接下来说说小编的场景&#xff0c;项目已经完成的差不多了&#xff0c;但是由于之前大家都是直接点击事件调用方法的…

第4章 4.3 EF Core 的实体类配置 Data Annatation Fluent API

4.3.1 约定大于配置 主要的约定规则&#xff1a; 规则 1: 数据库表名采用上下文类中对应的 DbSet 的属性名。 规则 2:数据库表列的名字采用实体类属性的名字&#xff0c;列的数据类型采用和实体类属性类型 兼容的类型。比如在 SQLServer 中&#xff0c;string 类型对应 nvarc…

【Redis 原理】通信协议 内存回收

文章目录 通信协议--RESP内存回收内存过期策略惰性删除周期删除 内存淘汰策略 通信协议–RESP Redis是一个CS架构的软件&#xff0c;通信一般分两步&#xff08;不包括pipeline和PubSub&#xff09;&#xff1a; 客户端&#xff08;client&#xff09;向服务端&#xff08;se…

【GreenHills】GHS合并库文件

1、 文档目标 解决Green Hills对于多个库文件合并问题 2、 问题场景 客户具有多个工程库文件。但是&#xff0c;客户想要在项目最终交付的时候&#xff0c;通过将多个库文件打包成一个库文件&#xff0c;进行交付。 3、软硬件环境 1&#xff09;、软件版本&#xff1a;MULTI…

山东大学软件学院nosql实验四

实验题目&#xff1a; 使用Java做简单数据插入 实验内容 用API方式&#xff0c;做数据插入。 使用Java语言实现数据插入界面&#xff0c;为实验一建立的学生、教师、课程表插入数据&#xff0c;可以在前端界面中录入数据之后保存&#xff0c;也可以导入Excel中的数据。 实…

nodejs npm install、npm run dev运行的坎坷之路

1、前面的种种都不说了&#xff0c;好不容易运行起来oap-portal项目&#xff0c;运行idm-ui项目死活运行不起来&#xff0c;各种报错&#xff0c;各种安装&#xff0c;各种卸载nodejs&#xff0c;卸载nvm&#xff0c;重装&#xff0c;都不好使。 2、甚至后来运行npm install会…

20250223下载并制作RTX2080Ti显卡的显存的测试工具mats

20250223下载并制作RTX2080Ti显卡的显存的测试工具mats 2025/2/23 23:23 缘起&#xff1a;我使用X99的主板&#xff0c;使用二手的RTX2080Ti显卡【显存22GB版本&#xff0c;准备学习AI的】 但是半年后发现看大码率的视频容易花屏&#xff0c;最初以为是WIN10经常更换显卡/来回更…

计算机毕业设计Hadoop+Spark+DeepSeek-R1大模型民宿推荐系统 hive民宿可视化 民宿爬虫 大数据毕业设计(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

centos 7 安装python3 及pycharm远程连接方法

安装openssl 使用pip3安装 virtualenv的时候会提示WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available. 这是因为缺少openssl 2.0以上版本 解决办法&#xff1a; 一、先确认版本 openssl version 二、安…

DeepSeek 助力 Vue 开发:打造丝滑的文本输入框(Text Input)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

Bybit最大资金盗窃事件技术分析 by CertiK

事件概述 2025年2月21日UTC时间下午02:16:11,Bybit的以太坊冷钱包(0x1db92e2eebc8e0c075a02bea49a2935bcd2dfcf4[1])因恶意合约升级遭到资金盗取。根据Bybit CEO Ben Zhou的声明[2],攻击者通过钓鱼攻击诱骗冷钱包签名者错误签署恶意交易。他提到,该交易被伪装为合法操作:…

欧拉筛法寻找素数与计算欧拉函数求和

欧拉筛法寻找素数与计算欧拉函数求和 一、欧拉函数1.1定义1.2性质1.3唯一分解定理&#xff08;算术基本定理&#xff09; 二、Eratosthenes筛法寻找素数三、欧拉筛法寻找素数3.1算法代码3.2算法分析3.2.1时间复杂度分析&#xff08;对合数进行不重复筛选&#xff09;3.2.2算法正…

VScode 开发

目录 安装 VS Code 创建一个 Python 代码文件 安装 VS Code VSCode&#xff08;全称&#xff1a;Visual Studio Code&#xff09;是一款由微软开发且跨平台的免费源代码编辑器&#xff0c;VSCode 开发环境非常简单易用。 VSCode 安装也很简单&#xff0c;打开官网 Visual S…