必须使用2dsphere索引配合$nearSphere,2d索引会报错;坐标顺序必须为[经度,纬度];$minDistance/$maxDistance单位为米;聚合中须用$geoNear且置首,spherical:true需显式声明。

必须用 2dsphere 索引,不能用 2d
$nearSphere 只能配合 2dsphere 索引工作,用 2d 索引会直接报错:error: { "ok": 0, "errmsg": "geoNear requires a 2dsphere index" }。这不是兼容性提示,而是硬性限制。
常见错误是看到文档里写“2d 和 2dsphere 都支持 $nearSphere”,但实际测试发现:MongoDB 4.4+ 已移除 2d 对 $nearSphere 的支持;即使旧版本允许,结果也会因平面模型导致距离严重失真(比如纽约到伦敦算出 500km)。
- 创建索引时必须指定字段值为 GeoJSON 或合法坐标对,例如:
db.restaurants.createIndex({ "location": "2dsphere" }) - 如果地理字段嵌套在子文档中(如
address.coord),索引路径要写全:db.restaurants.createIndex({ "address.coord": "2dsphere" }) - 一个集合只能有一个
2dsphere索引;若已有其他地理索引,得先dropIndex
坐标顺序写反会导致查询完全失效
使用 $geometry 时,coordinates 数组必须是 [经度, 纬度],不是 [纬度, 经度]。写反后 MongoDB 不报错,但返回结果为空或明显偏离——因为点被投射到了地球另一侧(比如把北京坐标 [116.4, 39.9] 写成 [39.9, 116.4],实际查的是南美洲附近)。
验证方法:用 db.collection.findOne() 检查样本文档的 location 字段结构,确认其 coordinates 是 [x, y] 格式且数值范围合理(经度 ∈ [-180, 180],纬度 ∈ [-90, 90])。
- 正确示例:
{ type: "Point", coordinates: [-73.9667, 40.78] } - 错误示例:
{ type: "Point", coordinates: [40.78, -73.9667] } - 驱动层(如 Node.js)也要检查传入的数组顺序,别让前端 JS 的
[lat, lng]习惯带进后端查询
$minDistance 和 $maxDistance 单位必须是米
当 $nearSphere 使用 $geometry(即 GeoJSON 点)时,$minDistance 和 $maxDistance 的单位固定为**米**,不是千米、不是角度、不是像素。设成 5 就是 5 米,设成 10000 就是 10 公里。
容易踩的坑是误以为和 $centerSphere 一样支持弧度,或者照搬旧版文档里 legacy 坐标对的用法(legacy 模式下单位才是弧度)。一旦单位错,$maxDistance: 10 可能过滤掉所有结果——因为 10 弧度 ≈ 6371km,远超地球半径。
- 安全写法:
$maxDistance: 5000表示 5km 范围内 - 组合使用
$minDistance时注意:它只在 MongoDB 4.0+ 支持,低版本忽略该参数 - 如果业务需要“排除最近 100 米内的点”,必须用
$minDistance: 100,不能靠应用层过滤,否则排序和分页会出错
聚合管道里必须用 $geoNear,不能用 find + $nearSphere
在 aggregate() 中想获取每个文档的精确距离值、做后续 $match 或 $sort,必须用 $geoNear 阶段。直接在 $match 里写 $nearSphere 不会输出距离字段,也**无法与 $addFields 或 $project 配合计算相对距离**。
典型错误是以为 $nearSphere 在聚合里也能自动附带距离,结果发现 calculatedDistance 字段为空,或者排序不生效。
- 正确写法:
{ $geoNear: { near: { type: "Point", coordinates: [-73.9667, 40.78] }, distanceField: "dist", spherical: true, maxDistance: 5000 } } -
spherical: true必须显式设置,否则默认按平面算(即使有2dsphere索引) -
$geoNear必须是 pipeline 的第一个阶段,且一个 pipeline 只能出现一次
真正麻烦的不是语法,而是数据本身:如果 location 字段混着 GeoJSON 对象、空值、字符串坐标甚至 null,2dsphere 索引会静默跳过这些文档,但查询时可能返回意外空集——得先用 db.collection.countDocuments({ location: { $type: "object" } }) 确认有效地理数据比例。


















