$geonear是聚合管道中唯一能计算距离、排序并注入距离字段的阶段,必须作为第一阶段、配合2dsphere索引使用,且需正确配置distancemultiplier和索引路径以避免单位混淆与查询失败。

$geoNear 是聚合管道中唯一能同时计算距离、排序并注入距离字段的阶段,必须配合 2dsphere 索引使用,且不能与 $text 等特殊索引操作混用。
必须先建 2dsphere 索引,否则 $geoNear 直接报错
没有索引时执行 $geoNear 会返回类似 "error: location is not indexed with 2dsphere" 的错误。MongoDB 不会自动创建索引,也不接受降级为平面计算。
- 索引字段名必须和
$geoNear中near所属的路径完全一致,比如文档是{ loc: { type: "Point", coordinates: [116.4, 39.9] } },索引就得建在loc上:db.places.createIndex({ loc: "2dsphere" }) - 如果集合里已有多个地理字段(如
loc和backupLoc),且都建了2dsphere索引,$geoNear必须显式指定key: "loc",否则报错"ambiguous geo index" - 索引只能建在单个字段上,不支持复合地理索引;若字段是嵌套结构(如
address.geo),索引路径就得写成{"address.geo": "2dsphere"}
$geoNear 的 distanceField 和 distanceMultiplier 怎么配单位
默认返回的距离单位是「球面弧度」,不是米——这点极易被忽略,导致前端显示“距离 0.001 km”这种明显错误。
-
distanceField: "dist"只是把算出的弧度值存进新字段dist,不做单位转换 - 要转成米,必须加
distanceMultiplier: 6371000(地球平均半径,单位米);转成公里就用6371 - 如果数据本身用的是 WGS84 坐标系(标准 GeoJSON),
spherical: true必须设为true,否则按平面欧氏距离算,长距离偏差可达 10% 以上 -
maxDistance参数单位始终与distanceMultiplier一致:设了distanceMultiplier: 6371,那maxDistance: 5就表示 5 公里
为什么 $geoNear 必须是 pipeline 第一阶段
$geoNear 是 MongoDB 唯一强制要求放在聚合开头的阶段。放错位置会直接报错 "$geoNear must be the first stage in the pipeline"。
- 它需要独占索引扫描权,后续阶段(如
$match、$project)只能做结果过滤或加工,不能参与地理筛选 - 想在查“附近餐厅”时再筛“评分 ≥4”,得写成:
{ $geoNear: { ... } }, { $match: { rating: { $gte: 4 } } },不能把$match提前 - 分页必须用
$skip/$limit放在$geoNear后面,但注意:先$limit: 10再$skip: 20是无效的——必须$skip在前、$limit在后,否则跳过的不是第 21–30 条,而是随机 20 条
$geoNear 与 $near 查询的实质区别
$near 是 find() 查询的操作符,$geoNear 是聚合阶段——二者能力边界清晰,不能互相替代。
-
$near返回结果天然按距离升序排,但无法把距离值回传给应用;$geoNear能通过distanceField把精确距离写入结果文档,方便前端展示“距您 1.2 km” -
$near不支持$minDistance(只支持$maxDistance),而$geoNear同时支持minDistance和maxDistance,可排除太近(比如自己店铺)或太远的点 - 分片集群中,
$near从 MongoDB 4.0 起才支持,此前只能靠$geoNear;现在虽都支持,但$geoNear仍是分片环境下更稳定的选择 -
$near无法和$text共存,$geoNear同样不行——但你可以用$geoWithin+$text组合绕过,只是不再按距离排序
真正容易被卡住的地方,是单位混淆和索引路径错位:一个没写 distanceMultiplier,另一个索引建在 location 却在 $geoNear 里写 key: "loc",两个小错误就能让整个查询返回空或乱序结果。











