不能直接用2d索引做“附近的人”,因其仅支持平面坐标系,忽略地球曲率导致百米级误差,且无法处理跨本初子午线或两极查询;必须使用2dsphere索引,字段需为geojson格式(如{"type":"point","coordinates":[lng,lat]}),且$near查询应配合$maxdistance(单位:米)以避免全表扫描。

为什么不能直接用 2d 索引做“附近的人”
因为 2d 只支持平面笛卡尔坐标系,把经纬度当成普通 x/y 处理,地球曲率被忽略,5km 以内的距离误差可能达百米级,且无法正确处理跨本初子午线(如东京和夏威夷)或两极附近的查询。2dsphere 才是 MongoDB 唯一支持球面几何运算的索引类型,所有 $near、$geoWithin、$geoIntersects 等地理操作都依赖它。
如何创建合法的 2dsphere 索引
字段必须是 GeoJSON 格式,不是简单的 [lng, lat] 数组。MongoDB 会拒绝为非 GeoJSON 字段建立 2dsphere 索引,报错:can't parse geometry: type 'Point' not found。
实操建议:
- 确保位置字段结构为:
{"location": {"type": "Point", "coordinates": [116.3974, 39.9093]}}注意顺序是[longitude, latitude],反了会导致位置偏移到非洲内陆 - 建索引命令必须指定字段路径和类型:
db.users.createIndex({"location": "2dsphere"}) - 如果已有旧数据是
[lng, lat]数组,需批量更新为 GeoJSON 格式,不能靠索引自动转换
$near 查询必须带 $maxDistance 吗
不强制,但生产环境几乎必须加。不设上限时,$near 会扫描全量匹配文档再排序,哪怕只取前 10 条,性能会随集合增长急剧恶化。
关键细节:
-
$maxDistance单位是**米**,不是公里或弧度 —— 写1000表示 1km 范围内 - 必须配合
geometry使用,例如:{"location": {"$near": {"$geometry": {"type": "Point", "coordinates": [116.3974, 39.9093]}, "$maxDistance": 1000}}} - 若同时需要按其他字段排序(如按活跃度),
$near必须是 sort 的唯一依据,否则索引失效;可先用$near限距查 ID,再用$in二次查详情
聚合管道里怎么安全用 $geoNear
$geoNear 是聚合阶段,比 $near 更灵活,但限制更严:必须是 pipeline 的第一个 stage,且集合只能有一个 2dsphere 索引(否则报错 geoNear requires exactly one 2dsphere index)。
常见陷阱:
- 误把
$geoNear放在$match后面 → 直接失败,MongoDB 不允许 - 想按距离分页?
$skip/$limit必须放在$geoNear之后,但深度分页(如第 100 页)仍会先算出全部结果再截断,应改用minDistance+ 上一页最大距离作为游标 - 返回字段中
distance默认单位是**米**,但若集合坐标系是 WGS84(标准经纬度),这个值就是球面测地距离,无需额外换算











