$geoNear必须作为聚合pipeline第一个stage;spherical: true必须显式指定;distanceField需显式命名以获取距离值;key选项在多地理索引时强制要求。

$geoNear 必须放在聚合 pipeline 开头
这是最常踩的硬性限制:$geoNear 只能作为第一个 stage 出现在 aggregate() 中,否则会报错 "$geoNear must be the first stage"。它不是普通操作符,而是触发地理空间扫描的入口点,MongoDB 要求它先接管查询计划。
常见误用场景包括:想先 $match 过滤类型再算距离,或想在 $lookup 后做附近查找——这些都得把 $geoNear 拿到最前,后续用 $match 筛选其他字段。
- 正确顺序:
$geoNear→$match→$sort→$limit - 错误写法:
$match→$geoNear(直接失败) - 若需关联其他集合,
$lookup必须放在$geoNear之后,且不能用于“先关联再算距离”逻辑
spherical: true 是必须显式写的,不是默认值
哪怕你用的是 GeoJSON + 2dsphere 索引,$geoNear 的 spherical 选项默认仍是 false。不设它,MongoDB 就按平面坐标算距离,高纬度偏差可达几十公里。
验证方法:查一个已知坐标的点(比如北京天安门 [116.3974, 39.9093]),对比设与不设 spherical: true 时返回的 dist 字段值——后者通常小一个数量级,明显不对。
- 必须写:
spherical: true - 单位自动匹配:GeoJSON 输入下,
maxDistance、minDistance和输出distanceField全部是**米** - 别信文档里“自动推断”的说法,实测 7.0+ 版本仍需手动指定
distanceField 是可选的,但没它就拿不到距离数值
$geoNear 默认不把计算出的距离塞进结果文档。如果你只想要“最近的 10 家咖啡馆”,不关心具体多少米,那可以省;但只要需要排序、分页、前端展示距离数字,就必须用 distanceField 显式命名一个字段。
注意:从 MongoDB 8.1 起,对非时间序列集合,distanceField 才变成可选;老版本或时间序列集合上它是必填项,漏写会报错 "distanceField is required"。
- 推荐写法:
distanceField: "distance",这样每个文档多一个distance字段,单位米 - 如果字段名含点号(如
"meta.distance"),MongoDB 会自动创建嵌套结构 - 别用
distance以外的常见名(如dist)——某些 ODM 库会默认读distance,避免额外映射
key 选项在多地理索引时不是可选,是强制项
如果你的集合同时有 {"loc": "2dsphere"} 和 {"venue.geo": "2dsphere"} 两个索引,执行 $geoNear 时必须用 key 指明用哪个字段,否则报错 "Ambiguous geo index"。
即使只有一个 2dsphere 索引,也建议显式写 key: "location" —— 防止后续加索引时突然崩,也方便别人一眼看懂查的是哪个字段。
- 写法示例:
key: "address.location"(对应文档中{"address": {"location": {...}}}) - 字段路径必须和
createIndex()时完全一致,大小写、点号都不能错 - 别指望 MongoDB 自动猜:它优先找
2d索引,找不到才找2dsphere,而你很可能根本没建2d索引
实际跑通的关键就这四点:stage 位置、spherical 开关、distanceField 命名、key 路径。少一个,要么报错,要么结果错得离谱却无提示。尤其 spherical: true 和坐标顺序([lng, lat]),这两个坑连经验开发者也会反复栽。











