必须用2dsphere索引,因为wgs84坐标系要求球面几何计算,2d索引仅适用于平面投影,会导致高纬度距离失真;2dsphere支持全套地理操作符且基于球面距离,而2d不支持多边形等geojson类型。

必须用 2dsphere 索引,而不是 2d —— 否则距离计算会出错,尤其在跨纬度或大范围查询时。
为什么 2dsphere 是唯一可靠选择
WGS84 坐标系(GPS 标准)要求球面几何计算,2d 索引只适用于平面投影,它把地球当成一张平铺的纸,导致高纬度地区距离严重失真。比如在哈尔滨附近查 5km 范围,2d 可能漏掉实际在范围内的点,或误召回几十公里外的点。
-
2dsphere支持$near、$geoWithin、$geoIntersects全套地理操作符,且全部基于球面距离 -
2d仅支持$near和$within的矩形/圆形,不支持多边形或 GeoJSON 多边形 - Laravel 的
geospatialIndex()默认创建2dsphere,但手动建索引时务必显式写"2dsphere",别省略引号
coordinates 字段必须是合法 GeoJSON Point
MongoDB 要求地理字段严格符合 GeoJSON 格式,否则 2dsphere 索引无法生效,查询会退化为全表扫描,explain() 显示 stage: COLLSCAN。
- 正确格式:
{"type": "Point", "coordinates": [116.4042, 39.915]}(注意:经度在前,纬度在后) - 常见错误:存成数组
[39.915, 116.4042](纬度经度颠倒)、或对象{"lng": 116.4042, "lat": 39.915}(非 GeoJSON) - Laravel 模型中用
'coordinates' => 'geojson'强制转换,比手写 mutator 更可靠
复合索引能显著加速带条件的地图查询
纯位置查询快,但真实场景往往要“附近 + 状态 + 类型”,比如“北京市朝阳区 3km 内营业中的咖啡馆”。这时单靠 2dsphere 索引不够,MongoDB 仍需过滤大量文档。
- 推荐复合索引顺序:
{ "coordinates": "2dsphere", "status": 1, "category": 1 } - 不能把
status放最前——2dsphere字段必须是复合索引的第一个字段,否则索引无效 - 验证是否命中:执行
db.locations.explain("executionStats").find({...}),确认executionStages.stage === "IXSCAN"且nReturned远小于totalDocsExamined
$maxDistance 必须配合 $near 使用,不能单独用
很多人想用 whereRaw({ coordinates: { $geoWithin: { $centerSphere: [[...], radius] } } }) 实现圆形范围,但没加 $near 就没法利用索引排序,性能极差。
- 正确写法必须含
$near:{"coordinates": {"$near": {"$geometry": {...}, "$maxDistance": 5000}}} -
$maxDistance单位是米,不是公里,传5就是 5 米,不是 5km - 如果不需要按距离排序,只关心“是否在范围内”,用
$geoWithin+$centerSphere也可以,但必须确保索引存在且字段格式正确,否则照样慢
最容易被忽略的是坐标顺序和单位——经度纬度写反、$maxDistance 当成公里、索引类型拼错成 "2dsphere "(尾部空格),这些都会让索引完全失效,而日志里不会报错,只会默默变慢。











