一、MongoDB 计算距离的优势
优势 | 说明 |
---|---|
原生地理空间索引 | 支持 2dsphere 索引,高效处理地理坐标查询(毫秒级响应)。 |
内置地理计算函数 | 提供 $near 、$geoWithin 、$geoNear 等操作符,无需手动实现复杂计算。 |
高性能 | 基于B树索引优化,海量数据下仍能快速返回结果(如亿级点位数据)。 |
灵活的数据模型 | 直接存储GeoJSON格式坐标,支持点、线、多边形等复杂地理形状。 |
与Spring生态集成 | 通过Spring Data MongoDB可简化开发。 |
二、实现步骤(Spring Boot + MongoDB)
1. 添加依赖
略,可参照前面MongoDB的博客。
2.定义地理位置实体
存储带有经纬度的文档(以"商家"为例):
java
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
索引(若未自动创建,可手动执行):
java
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公里内的商家(按距离排序)
java
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:查询多边形区域内的点位
java
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默认返回距离单位为米,需在业务层统一转换(如公里/英里)。
分页优化
java
NearQuery.near(userLocation)
.maxDistance(distance)
.skip(20) // 跳过前20条
.limit(10); // 每页10条