MongoDB GeoNear距离单位确认及有效范围数据过滤问题
问题描述
传入200作为距离参数,期望获取200范围内的MongoDB文档,但GeoNear实际以小于1.0000的值进行检查,导致所有文档都被返回。返回的siteToLineDistance值为0.004334109519106326,不清楚该值的单位,需要实现仅获取200米范围内文档的需求。
相关代码
Double latitude = requestDTO.getLatitude(); Double longitude = requestDTO.getLongitude(); String countryId = requestDTO.getCountryId(); String status = requestDTO.getStatusOfILM(); Point point = new Point(longitude, latitude); String geoSpatialQueryForLine = "{countryId:'" + countryId + "'},{ilmState:'" + status + "'}"; Query query = new BasicQuery(geoSpatialQueryForLine); List<AggregationOperation> list = new ArrayList<>(); NearQuery nearQuery = NearQuery.near(point).query(query).maxDistance(distance).minDistance(0).spherical(true); list.add(Aggregation.geoNear(nearQuery, RestServiceConfig.SITE_TO_LINE_DISTANCE)); list.add(Aggregation.project(RestServiceConfig.ID_KEY, RestServiceConfig.SITE_TO_LINE_DISTANCE)); TypedAggregation<TariffLineDocument> agg = new TypedAggregation<>(TariffLineDocument.class, list); List<TariffLineDocument> result = mongoOperations.aggregate(agg, TariffLineDocument.class).getMappedResults(); for (TariffLineDocument document : result) { TariffDocument tariffDocument = TariffDocument.builder() .tariffLayerId(document.getId()) .layerType(RestServiceConfig.LINE) .siteToLineDistance(document.getSiteToLineDistance()) .build(); documentList.add(tariffDocument); } logger.info(RestServiceConfig.NUMBER_OF_LINE_SHAPES, documentList.size()); } catch (Exception exception) { logger.info(RestServiceConfig.ERROR_IN_METHOD, exception.getMessage()); } return documentList;
问题分析与解决方案
核心问题
- 单位不匹配:当设置
spherical=true时,MongoDB的GeoNear默认以弧度作为距离单位,不是米/千米。你传入的200被当成了弧度,而200弧度对应的实际距离约为1,274,200公里(地球半径6371km × 200),远大于预期,所以所有文档都被返回。 - 查询条件拼接错误:原代码中
geoSpatialQueryForLine写成两个独立的JSON对象,BasicQuery的第二个参数是字段过滤条件,不是查询条件,导致ilmState的过滤根本没生效。
解决方案
1. 修正查询条件拼接
把两个查询条件合并成一个JSON对象:
String geoSpatialQueryForLine = "{countryId:'" + countryId + "', ilmState:'" + status + "'}";
2. 正确设置距离单位
有两种实现方式:
方式一:使用Spring Data的Metrics直接指定单位(推荐)
NearQuery提供带单位的方法,直接传入200米即可,无需手动换算:
NearQuery nearQuery = NearQuery.near(point) .query(query) .maxDistance(200, Metrics.METERS) // 明确指定距离单位为米 .minDistance(0) .spherical(true);
此时返回的siteToLineDistance单位也是米,和传入值一致。
方式二:手动将米转换为弧度
如果不想用Metrics,可通过公式距离(米) / 地球半径(米)换算成弧度,地球半径取6371000米:
double earthRadiusInMeters = 6371000; double maxDistanceInRadians = 200 / earthRadiusInMeters; // 约3.139e-5弧度 NearQuery nearQuery = NearQuery.near(point) .query(query) .maxDistance(maxDistanceInRadians) .minDistance(0) .spherical(true);
这种情况下返回的siteToLineDistance是弧度值,需要自行换算成米:弧度值 × 6371000。
修正后的完整代码
Double latitude = requestDTO.getLatitude(); Double longitude = requestDTO.getLongitude(); String countryId = requestDTO.getCountryId(); String status = requestDTO.getStatusOfILM(); Point point = new Point(longitude, latitude); // 修正查询条件,合并为一个JSON对象 String geoSpatialQueryForLine = "{countryId:'" + countryId + "', ilmState:'" + status + "'}"; Query query = new BasicQuery(geoSpatialQueryForLine); List<AggregationOperation> list = new ArrayList<>(); // 使用Metrics指定距离单位为米 NearQuery nearQuery = NearQuery.near(point) .query(query) .maxDistance(200, Metrics.METERS) .minDistance(0) .spherical(true); list.add(Aggregation.geoNear(nearQuery, RestServiceConfig.SITE_TO_LINE_DISTANCE)); list.add(Aggregation.project(RestServiceConfig.ID_KEY, RestServiceConfig.SITE_TO_LINE_DISTANCE)); TypedAggregation<TariffLineDocument> agg = new TypedAggregation<>(TariffLineDocument.class, list); List<TariffLineDocument> result = mongoOperations.aggregate(agg, TariffLineDocument.class).getMappedResults(); for (TariffLineDocument document : result) { TariffDocument tariffDocument = TariffDocument.builder() .tariffLayerId(document.getId()) .layerType(RestServiceConfig.LINE) .siteToLineDistance(document.getSiteToLineDistance()) .build(); documentList.add(tariffDocument); } logger.info(RestServiceConfig.NUMBER_OF_LINE_SHAPES, documentList.size()); } catch (Exception exception) { logger.info(RestServiceConfig.ERROR_IN_METHOD, exception.getMessage()); } return documentList;
内容的提问来源于stack exchange,提问作者Deepak
相关产品推荐
相关产品推荐

