Mongo 地理位置查询:海量密集点转换成聚合信息

通俗来说:将地图上的海量密集点通过网格分割的方式实现聚合;

需求:用mongo实现设备地理位置聚合查询 :多边形,矩形查询;

背景:上万设备数据量

目的:分享Mongo地理位置查询,以及文末对在此之前的两种实现方式做分析比较,纠正一些开发中的错误认知;

1、自定义:数据库查询 

//多边形   $polygon 
@Query(value = "{'position': {$exists: true}, $and: [{'position': {$geoWithin: { $polygon : ?0 }}}, {'deletedAt': null}]}" , fields = "{ 'position': 1 }")List<Thing> findByProvinceBoundary(double[][] provinceBoundary);//矩形 $box@CountQuery("{$and: [{'position': {$exists: true}}, {'deletedAt': null},"+ "{'position': {$geoWithin: { $box: [?0, ?1] }}}]}")long countByProvinceBoundaryOrBoundingBox(double[] bottomLeft, double[] topRight);

注意:MongoDB 不支持在一个查询中同时使用 $polygon 和 $box;

2、如果需要求中心点,可以使用聚合查询,实现加权平均权重点

 @Aggregation(pipeline = {"{ $match: { $and: [ { 'position': { $exists: true } }, { 'deletedAt': null }, { 'position': { $geoWithin: { $box: [?0, ?1] } } } ] } }","{ $group: { _id: null, longitude: { $avg: '$position.longitude' }, latitude: { $avg: '$position.latitude' } } }"})GeoPoint findWeightedCenter(double[] bottomLeft, double[] topRight);
3、完整设备位置聚合信息查询:最佳方案
 @Overridepublic ThingGeo getAggregatedThingGeo(ThingGeoReqDTO reqDTO) {Area area = areaRepository.getAreaByCode();//行政编码//1.行政编码区域的中心点,查询没有位置的设备总数:JSONObject properties = area.getBound().getJSONArray("features").getJSONObject(0).getJSONObject("properties");JSONArray centerPosition= properties.getJSONArray("center"); //中心点位置double centerLon = centerPosition.getDouble(0);double centerLat = centerPosition.getDouble(1);GeoPoint centerPoint = new GeoPoint(centerLon, centerLat);long noGeoThingCount = thingRepository.countByNoGeoPosition();GridCellThing noGeoThings = new GridCellThing(centerPoint,noGeoThingCount);//2.网格查询有位置信息的设备总数以及权重点double[] topRight = reqDTO.getTopRight();double[] bottomLeft = reqDTO.getBottomLeft();// 计算X和Y的差值(视图长和宽)double deltaX = topRight[0] - bottomLeft[0];double deltaY = topRight[1] - bottomLeft[1];// 计算X和Y的平均值double avgX = deltaX / 4;double avgY = deltaY / 4;// 使用右上角作为起始点double x = topRight[0];double y = topRight[1];List<GridCellThing> gridCellThings = new ArrayList<>();// 循环生成4*4=16网格for (int a = 0; a < 4; a++) {for (int i = 0; i < 4; i++) {// 计算网格边界double minX = x - (i + 1) * avgX;double maxX = x - i * avgX;double minY = y - (a + 1) * avgY;double maxY = y - a * avgY;//小网格:两个对角经纬度double[] boxTopRight = new double[] {maxX, maxY};double[] boxBottomLeft = new double[] {minX, minY};long boxCount = thingRepository.countByBoundingBox(boxBottomLeft,boxTopRight);if (boxCount > 0) {GeoPoint center = thingRepository.findWeightedCenter(boxBottomLeft, boxTopRight);GeoPoint boxCenter = new GeoPoint(center.getLongitude(),center.getLatitude());GridCellThing gridCellThing = new GridCellThing();gridCellThing.setThingCount(boxCount);gridCellThing.setPosition(boxCenter);gridCellThings.add(gridCellThing);}}}ThingGeo thingGeo = new ThingGeo();thingGeo.setGridCellList(gridCellThings);thingGeo.setNoGeoThings(noGeoThings);return thingGeo;}
4、在此之前的 踩坑错误实现代码:在数据量多的时候,导致内存溢出;

因为拿到几万条设备信息导致内存溢出;

 public static List<GridCellThing> getGridCellThings(ThingGeoReqDTO reqVO, List<Thing> things) {double[] topRight = reqVO.getTopRight();double[] bottomLeft = reqVO.getBottomLeft();// 计算X和Y的差值(视图长和宽)double deltaX = topRight[0] - bottomLeft[0];double deltaY = topRight[1] - bottomLeft[1];// 计算X和Y的平均值double avgX = deltaX / 4;double avgY = deltaY / 4;// 使用右上角作为起始点double x = topRight[0];double y = topRight[1];List<GridCellThing> gridCellThings = new ArrayList<>();// 循环生成4*4=16网格for (int a = 0; a < 4; a++) {for (int i = 0; i < 4; i++) {GridCellThing gridCellThing = new GridCellThing();// 计算网格边界double minX = x - (i + 1) * avgX;double maxX = x - i * avgX;double minY = y - (a + 1) * avgY;double maxY = y - a * avgY;double centerTotalX = 0;double centerTotalY = 0;int count = 0;// 遍历设备列表for (Thing thing : things) {double longitude = thing.getPosition().getLongitude();double latitude = thing.getPosition().getLatitude();// 检查设备是否在当前网格内,根据最大值最小值区分来做到去重边界相交的设备;if (longitude > minX && longitude <= maxX && latitude > minY && latitude <= maxY) {count++;//得到有效设备的经纬度总和centerTotalX += longitude;centerTotalY += latitude;}}// 如果有符合条件的设备,则记录到响应对象列表中if (count > 0) {//分别把经度总和、纬度总和 除以count(网格区域内设备总数)得到经纬度的加权平均值double centerLongitude = centerTotalX / count;double centerLatitude = centerTotalY / count;GeoPoint geoPoint = new GeoPoint();geoPoint.setLongitude(centerLongitude);geoPoint.setLatitude(centerLatitude);gridCellThing.setPosition(geoPoint);gridCellThing.setThingCount(count);gridCellThings.add(gridCellThing);}}}return gridCellThings;}

两种实现方式分析:

方法一:服务端实现 查询数据库后拿到上万设备信息后再在业务实现层进一步对数据进行循环过滤等操作,

方法二:在数据库查询以及在数据库实现其他操作;


在服务端实现设备地理位置聚合信息时,方法一和方法二的选择取决于多个因素,如性能、可维护性、复杂性和数据量。下面是对这两种方法的详细分析:

方法一:在业务实现层进行过滤和操作

优点

  1. 灵活性:在业务层处理数据,您可以更灵活地应用复杂的业务逻辑。
  2. 可维护性:业务逻辑和数据库查询逻辑分开,更易于管理和调试。
  3. 缓存机制:可以更容易地实现数据缓存,以减少频繁的数据库查询。

缺点

  1. 性能瓶颈:将大量数据加载到内存中进行处理,会占用大量的内存资源,可能导致性能瓶颈,尤其是在数据量很大的情况下。
  2. 网络开销:传输大量数据从数据库到应用服务器,会增加网络带宽的开销。
  3. 延迟:处理大量数据会增加响应时间,导致用户体验不佳。

方法二:在数据库中进行聚合和操作

优点

  1. 高效:数据库系统通常对大规模数据的处理进行了高度优化,能够更高效地执行聚合和过滤操作。
  2. 减少数据传输:只传输必要的聚合结果而不是原始数据,减少网络带宽的使用。
  3. 性能优势:数据库层面的操作可以利用索引、缓存等优化机制,提升查询性能。

缺点

  1. 复杂性:在数据库中实现复杂的业务逻辑可能会增加查询的复杂性,难以调试和维护。
  2. 数据库负载:将大量计算操作放在数据库中,可能增加数据库服务器的负载。
  3. 灵活性:可能需要编写复杂的数据库脚本,对于变更和扩展不如业务层处理灵活。

选择建议:

在大多数情况下,方法二(在数据库中进行聚合和操作)通常是首选,特别是在处理大数据量时,理由如下:

  1. 性能:数据库聚合操作通常比在业务层进行大规模数据处理更快。
  2. 减少数据传输:只传输必要的聚合结果,减少网络带宽的使用。
  3. 简化业务逻辑:让数据库处理繁重的数据操作,简化业务层的代码。

然而,也有一些场景可能更适合方法一:

 结论:

最终选择应根据实际情况、系统架构和业务需求综合考虑。

  1. 复杂业务逻辑:如果聚合逻辑非常复杂,数据库难以实现或者维护,可以考虑在业务层处理。
  2. 数据库负载:如果数据库负载已经很高,可能需要将部分处理移到应用层。
  3. 优先选择方法二:对于处理大量数据和需要高效聚合操作的场景,优先选择在数据库中进行操作。
  4. 灵活调整:根据具体业务需求和系统架构,灵活调整部分处理逻辑在业务层和数据库层之间的分配。

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

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

相关文章

zdppy_amauth 实现给角色批量绑定权限

新增接口 api.resp.post("/auth/role_auth", amauth.role.add_auths)如何测试 如何测试能不能给指定的角色批量的添加权限呢&#xff1f; 1、需要新建一个角色2、需要拿到这个角色的ID3、需要新增三个权限4、需要拿到新增的三个权限的ID5、拿着角色ID和权限ID列表…

SSL代码签名最佳实践

代码签名就是软件发布者使用全球可信的证书颁发机构CA颁发的代码签名证书对软件代码进行签名&#xff0c;由此来验证软件开发者的真实身份&#xff0c;确保软件代码的完整性和可信任性。然而&#xff0c;攻击者一直试图渗透代码签名&#xff0c;意将恶意软件嵌入可信代码中。由…

【二叉树】Leetcode 637. 二叉树的层平均值【简单】

二叉树的层平均值 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[3.00000,14.50000,11.00000] 解释&#xff1a…

前端 JS 经典:如何实现私有字段

前言&#xff1a;方法有很多&#xff0c;但是我们需要择优选择。 1. 命名规范 我们可以通过命名规范实现私有字段&#xff0c;如&#xff1a;下划线 缺点&#xff1a;没有强约束力&#xff0c;我们用了也就用了 class A {_key 1; }const a new A(); a._key; // 1 2. Sym…

深入探讨5种单例模式

文章目录 一、对比总览详细解释 二、代码1. 饿汉式2. 饱汉式3. 饱汉式-双检锁4. 静态内部类5. 枚举单例 三、性能对比 一、对比总览 以下是不同单例模式实现方式的特性对比表格。表格从线程安全性、延迟加载、实现复杂度、反序列化安全性、防反射攻击性等多个方面进行考量。 …

必应bing国内广告怎样开户投放呢?

企业都在寻找高效、精准的营销渠道以扩大品牌影响力&#xff0c;提升市场占有率&#xff0c;作为全球第二大搜索引擎&#xff0c;微软旗下的必应Bing凭借其卓越的搜索技术和庞大的用户基础&#xff0c;成为了众多企业拓展市场的首选广告平台。在中国&#xff0c;必应Bing广告以…

Day33

Day33 SQL-续 数据类型 tinyint、int、unsigned、float、double、decimal、char、varchar、BLOB、LONGBLOB、TEXT、LONGTEXT、date、time、datetime、timestamp、year # 数据类型# 整数类型 ------------------------------------------------------------- # tinyint - 1字节…

vuInhub靶场实战系列-DC-6实战

免责声明 本文档仅供学习和研究使用,请勿使用文中的技术源码用于非法用途,任何人造成的任何负面影响,与本人无关。 目录 免责声明前言一、环境配置二、信息收集2.1 主机发现2.1.1 nmap扫描存活主机2.1.2 arp-scan扫描存活主机 2.2 端口扫描2.3 指纹识别2.3.1 尝试指纹识别2.3.…

【LC刷题】DAY01:704. 二分查找、27. 移除元素

【LC刷题】DAY01&#xff1a;704. 二分查找、27. 移除元素 文章目录 【LC刷题】DAY01&#xff1a;704. 二分查找、27. 移除元素704. 二分查找 [link](https://leetcode.cn/problems/binary-search/description/)第一思路&#xff1a;优化思路 27. 移除元素 [link](https://leet…

解决Mac无法上网/网络异常的方法,重置网络

解放方法 1、前往文件夹&#xff1a;/Library/Preferences/SystemConfiguration 2 、在弹窗中输入上边的地址 3 、把文件夹中除了下图未选中的文件全部删掉&#xff0c;删除时需要输入密码 4 、重启mac 电脑就搞定了。

python的一种集成开发工具:PyCharm开发工具

一. 简介 本文简单了解两种 python语言所使用的 集成开发环境&#xff1a; PyCharm、vscode。 python语言学习中&#xff0c;可以任意选中这两个集成开发环境的一种就可以。本文先来简单学习 PyCharm开发工具安装与使用。 二. python的一种集成开发工具&#xff1a;PyChar…

【LeetCode】40. 组合总和 II

组合总和 II 题目描述&#xff1a; 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 示例…

数据更新后Ant Design Table组件界面未刷新怎么解决?

在使用Ant Design的Table组件时&#xff0c;我们时常需要动态更新表格数据以满足应用需求。然而&#xff0c;有时即使数据已经更新&#xff0c;界面却没有进行相应的刷新。这种情况可能由多种因素导致&#xff0c;其中之一就是关于key属性的使用问题。本文将重点讨论key属性在解…

Nginx的https功能

一.HTTPS功能简介 Web网站的登录页面都是使用https加密传输的&#xff0c;加密数据以保障数据的安全&#xff0c;HTTPS能够加密信息&#xff0c;以免敏感信息被第三方获取&#xff0c;所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议&#xff0c;HTTPS其实…

Springboot框架开发与实用篇之热部署 2024详解

开发与实用 手动启动热部署 热部署&#xff08;Hot Deployment&#xff09;指的是在应用程序正在运行的情况下&#xff0c;对其进行更新或修改并将这些变更应用到正在运行的应用程序中的过程。通常情况下&#xff0c;传统的部署方式需要停止应用程序、部署更新&#xff0c;然…

基于51单片机的智能晾衣架设计资料

第三章:硬件单元电路 经过上述分析明确了本次设计的主要目标,为了实现晾衣自身能够完成对外界数据的采集与分析,集成控制环节我们采用了ATMEL公司生产的AT89C52单片机,与市面上的其他嵌入式控制单元相比较在体积与功耗方面都相当出色。此次设计主要突破在于设计合理的控制电…

最短路问题

最短路问题是图论里非常经典的一个考点 接下来着重讲述五种求最短路的算法&#xff1a;朴素版dijkstra算法、堆优化版的dijkstra算法、bellman-ford算法、spfa算法、floyd算法 总体思维导图&#xff1a; 总体思路&#xff1a; 最短路分为两大类 { 在以下给出的时间复杂度中n…

学习笔记——路由网络基础——静态路由(static)

三、静态路由(static) 1、静态路由 (1)定义 静态路由(Static)&#xff1a;由管理员手动配置和维护的路由。静态路由配置简单&#xff0c;被广泛应用于网络中。此外还可以实现负载均衡和路由备份。 静态路由默认优先级为60&#xff0c;如果想在多条静态路由中让某条路由优选…

网络运维:现代IT架构的守护者

网络运维&#xff1a;现代IT架构的守护者 在数字化浪潮席卷全球的今天&#xff0c;网络运维已不再是简单的技术术语&#xff0c;而是每一个追求高效、稳定运营的企业和组织必须重视的关键环节。本文将探讨网络运维的重要性&#xff0c;以及它在现代IT架构中所扮演的不可或缺的…

YOLOX源码之 Label Assignment

网络结构没什么好讲的&#xff0c;backbone、neck、head组成&#xff0c;backbone采用的cspdarknet&#xff0c;neck采用的pafpn&#xff0c;head是decoupled head结构。这里主要讲一下label assignment的具体实现&#xff0c;yolox中采用了simota&#xff0c;是ota的简化版本。…