做网站需要学什么软件,定制开发小程序和模板小程序,专业设计自学网站,商务网站的特点通俗来说#xff1a;将地图上的海量密集点通过网格分割的方式实现聚合#xff1b;
需求#xff1a;用mongo实现设备地理位置聚合查询 #xff1a;多边形#xff0c;矩形查询;
背景#xff1a;上万设备数据量
目的#xff1a;分享Mongo地理位置查询#xff0c;以及文…通俗来说将地图上的海量密集点通过网格分割的方式实现聚合
需求用mongo实现设备地理位置聚合查询 多边形矩形查询;
背景上万设备数据量
目的分享Mongo地理位置查询以及文末对在此之前的两种实现方式做分析比较纠正一些开发中的错误认知
1、自定义数据库查询
//多边形 $polygon
Query(value {position: {$exists: true}, $and: [{position: {$geoWithin: { $polygon : ?0 }}}, {deletedAt: null}]} , fields { position: 1 })ListThing findByProvinceBoundary(double[][] provinceBoundary);//矩形 $boxCountQuery({$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];ListGridCellThing gridCellThings new ArrayList();// 循环生成4*416网格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 ListGridCellThing getGridCellThings(ThingGeoReqDTO reqVO, ListThing 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];ListGridCellThing gridCellThings new ArrayList();// 循环生成4*416网格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;} 两种实现方式分析
方法一服务端实现 查询数据库后拿到上万设备信息后再在业务实现层进一步对数据进行循环过滤等操作
方法二在数据库查询以及在数据库实现其他操作 在服务端实现设备地理位置聚合信息时方法一和方法二的选择取决于多个因素如性能、可维护性、复杂性和数据量。下面是对这两种方法的详细分析
方法一在业务实现层进行过滤和操作
优点
灵活性在业务层处理数据您可以更灵活地应用复杂的业务逻辑。可维护性业务逻辑和数据库查询逻辑分开更易于管理和调试。缓存机制可以更容易地实现数据缓存以减少频繁的数据库查询。
缺点
性能瓶颈将大量数据加载到内存中进行处理会占用大量的内存资源可能导致性能瓶颈尤其是在数据量很大的情况下。网络开销传输大量数据从数据库到应用服务器会增加网络带宽的开销。延迟处理大量数据会增加响应时间导致用户体验不佳。
方法二在数据库中进行聚合和操作
优点
高效数据库系统通常对大规模数据的处理进行了高度优化能够更高效地执行聚合和过滤操作。减少数据传输只传输必要的聚合结果而不是原始数据减少网络带宽的使用。性能优势数据库层面的操作可以利用索引、缓存等优化机制提升查询性能。
缺点
复杂性在数据库中实现复杂的业务逻辑可能会增加查询的复杂性难以调试和维护。数据库负载将大量计算操作放在数据库中可能增加数据库服务器的负载。灵活性可能需要编写复杂的数据库脚本对于变更和扩展不如业务层处理灵活。
选择建议
在大多数情况下方法二在数据库中进行聚合和操作通常是首选特别是在处理大数据量时理由如下
性能数据库聚合操作通常比在业务层进行大规模数据处理更快。减少数据传输只传输必要的聚合结果减少网络带宽的使用。简化业务逻辑让数据库处理繁重的数据操作简化业务层的代码。
然而也有一些场景可能更适合方法一 结论 最终选择应根据实际情况、系统架构和业务需求综合考虑。 复杂业务逻辑如果聚合逻辑非常复杂数据库难以实现或者维护可以考虑在业务层处理。数据库负载如果数据库负载已经很高可能需要将部分处理移到应用层。优先选择方法二对于处理大量数据和需要高效聚合操作的场景优先选择在数据库中进行操作。灵活调整根据具体业务需求和系统架构灵活调整部分处理逻辑在业务层和数据库层之间的分配。