2dsphere索引要求坐标必须为标准GeoJSON格式,如{"type":"Point","coordinates":[-73.9857,40.7484]},经度在前、纬度在后,范围分别为[-180,180]和[-90,90];不支持{lat:xx,lng:xx}等自定义结构,建索引前需确保字段合法且非空,否则报错。

插入带坐标的文档必须用 GeoJSON 格式
MongoDB 的 2dsphere 索引只认标准 GeoJSON 对象,不能直接存 { lat: 40.7128, lng: -74.0060 } 这类自定义结构。常见错误是字段名写成 latitude/longitude 或顺序颠倒(经度必须在前),导致索引无法命中或查询报错 location object expected。
正确写法是用 type: "Point" + coordinates: [lng, lat],注意数组顺序:先经度、再纬度:
{
"name": "Central Park",
"location": {
"type": "Point",
"coordinates": [-73.9857, 40.7484]
}
}
- 坐标单位为十进制度数,范围:经度
[-180, 180],纬度[-90, 90] -
location字段名可自定义,但后续建索引和查询时要保持一致 - 如果数据来自前端
Geolocation API,注意它返回的是{ latitude, longitude },需手动转成[longitude, latitude]
建 2dsphere 索引前必须确保字段存在且格式合法
执行 db.collection.createIndex({ "location": "2dsphere" }) 时,MongoDB 会扫描所有文档校验 location 字段是否为有效 GeoJSON。只要有一条文档的 location 是 null、空对象、坐标越界或类型错误,建索引就会失败并报错 can't parse geometry。
- 建议先清理或跳过异常数据:
db.collection.find({ "location.type": { $exists: true } }).count() - 若字段可能为空,建索引时加选项跳过:
db.collection.createIndex({ "location": "2dsphere" }, { sparse: true }) - 索引名称默认是字段名+类型,如需自定义可用
name: "loc_2dsphere"参数
查询附近点要用 $near 或 $geoWithin,别用 $where
$near 是唯一支持排序(按距离升序)的地理查询操作符,且必须配合 2dsphere 索引;$geoWithin 适合查多边形/圆形覆盖范围内的点。误用 $where 写距离计算(比如用勾股定理算球面距离)会导致全表扫描,性能极差,且结果不准确。
例如查 1km 内的地点:
db.places.find({
location: {
$near: {
$geometry: { type: "Point", coordinates: [-73.9857, 40.7484] },
$maxDistance: 1000
}
}
})
-
$maxDistance单位是米,仅对$near有效;$geoWithin需用$centerSphere配合弧度 -
$near必须是查询条件中唯一的排序依据,不能和其他$sort混用 - 聚合管道中要用
$geoNear阶段,且必须是第一个阶段
更新地理位置字段时容易忽略索引失效风险
用 updateOne 直接替换整个 location 对象没问题,但若用 $set 只更新子字段(如 $set: { "location.coordinates.0": -73.985 }),MongoDB 不会自动校验新值是否仍符合 GeoJSON 规范,可能导致后续查询因数据损坏而静默失败。
- 安全做法是始终用完整 GeoJSON 对象更新:
$set: { location: { type: "Point", coordinates: [...] } } - 批量导入时,建议先用
db.collection.validate()检查索引完整性 - 线上环境修改 schema 前,务必在副本集 secondary 节点验证索引重建行为,避免主节点锁表时间过长
GeoJSON 格式约束和索引校验逻辑藏得深,出问题往往不是语法错,而是坐标顺序、空值处理或更新方式不符合 MongoDB 的隐式契约。


















