为什么2dsphere索引是地理范围查询的必需选择
不建2dsphere索引,$geoWithin、$near这类操作基本没法用——要么报错error: { "ok": 0, "errmsg": "geoNear requires a 2dsphere index...",要么全表扫描,几万条数据就卡住。MongoDB只允许对Point、LineString、Polygon等GeoJSON类型字段使用地理查询,且必须通过2dsphere索引加速,2d索引仅支持平面坐标(如经纬度数组),不适用于真实球面距离计算。
如何正确创建2dsphere索引并验证结构
关键不是“加索引”,而是字段值必须是合法GeoJSON格式,且索引路径要精确匹配。常见错误是把{ lng: 116.4, lat: 39.9 }直接存进去,这不会被识别为地理位置。
确保文档中位置字段是标准GeoJSON
Point:例如{ "location": { "type": "Point", "coordinates": [116.4, 39.9] } }(注意顺序是[longitude, latitude])创建索引命令:
db.places.createIndex({ "location": "2dsphere" })验证是否生效:
db.places.getIndexes(),确认输出中存在{ "location": "2dsphere" }条目如果已有旧格式数据(如
{ loc: [116.4, 39.9] }),需批量转换:db.places.updateMany({ loc: { $exists: true } }, [ { $set: { location: { type: "Point", coordinates: "$loc" } } }, { $unset: "loc" } ])
$geoWithin和$near的实际写法与性能差异
两者都依赖2dsphere索引,但语义和行为完全不同,选错会导致结果偏差或无法排序。
$geoWithin只做“是否在范围内”判断,适合查某个多边形区域内的所有点,不返回距离信息:{ location: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [[[116.3,39.8],[116.5,39.8],[116.5,40.0],[116.3,40.0],[116.3,39.8]]] } } } }$near强制要求排序,且必须配合$maxDistance才能实现“附近N公里内”,否则返回整个集合按距离排序:{ location: { $near: { $geometry: { type: "Point", coordinates: [116.4, 39.9] }, $maxDistance: 1000 } } }(单位:米)注意:
$near不能和$sort混用,也不能在$or中使用;而$geoWithin可与其他条件组合,但$near必须出现在查询顶层
容易被忽略的精度与投影问题
MongoDB内部使用WGS84椭球模型,但不校正地形起伏或建筑物遮挡——它算的是球面直线距离(大圆距离),不是实际道路距离。这意味着:
高纬度地区(如北纬60°以上)的
coordinates数值微小变化,对应实际距离可能放大2倍以上,$maxDistance: 1000在赤道≈1km,在北极圈可能只有500m覆盖效果不要试图用
2dsphere索引做高精度室内定位(如楼层、房间),它的设计目标是城市/郊区级地理围栏若业务需要路网距离,必须在应用层调用地图API(如高德/Mapbox Directions)二次计算,数据库只负责初筛
