使用MongoDB存储和计算距离
一、MongoDB 计算距离的优势
优势 | 说明 |
---|---|
原生地理空间索引 | 支持 2dsphere 索引,高效处理地理坐标查询(毫秒级响应)。 |
内置地理计算函数 | 提供 $near 、$geoWithin 、$geoNear 等操作符,无需手动实现复杂计算。 |
高性能 | 基于B树索引优化,海量数据下仍能快速返回结果(如亿级点位数据)。 |
灵活的数据模型 | 直接存储GeoJSON格式坐标,支持点、线、多边形等复杂地理形状。 |
与Spring生态集成 | 通过Spring Data MongoDB可简化开发。 |
二、实现步骤(Spring Boot + MongoDB)
1. 添加依赖
略,可参照前面MongoDB的博客。
2.定义地理位置实体
存储带有经纬度的文档(以“商家”为例):
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.mapping.Document;@Document(collection = "places")
@Data
public class Place {private String id;private String name;@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) // 关键:创建地理空间索引private GeoJsonPoint location; // 格式: { type: "Point", coordinates: [经度, 纬度] }}
3. 初始化地理空间索引
确保集合已创建 2dsphere
索引(若未自动创建,可手动执行):
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.index.GeospatialIndex;@PostConstruct
public void initIndex() {mongoTemplate.indexOps(Place.class).ensureIndex(new GeospatialIndex("location"));
}
4. 实现距离查询
场景1:查询附近5公里内的商家(按距离排序)
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;public List<Place> findNearbyPlaces(double longitude, double latitude, double maxDistanceKm) {Point userLocation = new Point(longitude, latitude);Distance distance = new Distance(maxDistanceKm, Metrics.KILOMETERS);NearQuery nearQuery = NearQuery.near(userLocation).maxDistance(distance).spherical(true); // 使用球面几何计算return mongoTemplate.find(new Query(Criteria.where("location").nearSphere(userLocation).maxDistance(distance.getNormalizedValue())),Place.class);
}
场景2:查询多边形区域内的点位
import org.springframework.data.geo.Polygon;public List<Place> findWithinPolygon(List<Point> polygonPoints) {return mongoTemplate.find(Query.query(Criteria.where("location").within(new Polygon(polygonPoints))),Place.class);
}
三、MongoDB地理查询原理解析
-
索引类型
-
2dsphere
:支持球面几何计算(地球曲率),适合真实地理场景。 -
2d
:平面计算(简化模型),适用于小范围地图(如游戏地图)。
-
-
距离算法
MongoDB默认使用 Haversine公式 计算球面距离,精度高。 -
性能对比
-
MySQL:需手动计算
ST_Distance_Sphere
,无专用索引,性能随数据量下降。 -
MongoDB:利用地理索引,查询时间稳定在毫秒级。
-
四、注意事项
-
坐标格式标准化
-
始终使用
[经度, 纬度]
顺序(GeoJSON标准),避免混淆。 -
示例:
new GeoJsonPoint(116.404, 39.915)
(北京天安门)。
-
-
单位一致性
-
MongoDB默认返回距离单位为米,需在业务层统一转换(如公里/英里)。
-
-
分页优化
NearQuery.near(userLocation).maxDistance(distance).skip(20) // 跳过前20条.limit(10); // 每页10条