2dsphere索引必须建在严格符合geojson规范的字段上,即形如{"type":"point","coordinates":[lng,lat]},顺序为经度在前、纬度在后,范围分别为[-180,180]和[-90,90];普通数组[lng,lat]或{lng:,lat:}等非geojson结构虽可建索引但查询会静默失败。

为什么2dsphere索引必须建在GeoJSON格式字段上
直接对普通经纬度数组(如 [lng, lat])或嵌套的 { longitude: x, latitude: y } 结构建 2dsphere 索引会失败,MongoDB 要求字段值严格符合 GeoJSON 规范。最常见错误是插入时用错结构,比如写成 { location: { lat: 39.9, lng: 116.3 } } —— 这种格式不被识别为地理点,索引虽能创建,但查询时 $near、$geoWithin 全部返回空结果。
正确做法是确保数据为标准 GeoJSON Point:
{
"location": {
"type": "Point",
"coordinates": [116.3, 39.9] // 注意:顺序是 [longitude, latitude]
}
}
- 字段名(如
location)可自定义,但值必须含type和coordinates -
coordinates数组长度必须为 2,且第一个是经度(-180~180),第二个是纬度(-90~90) - 如果已有旧数据,需用
updateMany批量转换,不能靠索引自动适配
创建2dsphere索引的实际命令和参数
索引创建本身很简单,但参数选错会导致查询不可用或性能异常。核心命令就是 createIndex,但必须指定 "2dsphere" 类型,不能写成 "2d"(那是旧版平面索引,不支持球面距离计算)。
基础命令:
db.places.createIndex({ "location": "2dsphere" })
- 如果字段路径更深(如
address.geo),索引键写成{"address.geo": "2dsphere"} - 生产环境建议加
background: true,避免阻塞写入:db.places.createIndex({ "location": "2dsphere" }, { background: true }) - 不要加
sparse: true除非你确定该字段允许缺失——否则$geoNear可能跳过部分文档
$near 查询必须带 $geometry 且单位是弧度?
不是。MongoDB 的 $near 默认使用米(meter)为单位,但前提是查询中明确指定 GeoJSON 结构,且索引字段类型匹配。常见错误是直接传数组或对象:
// ❌ 错误:这会报错 "unknown operator: $near"
{ location: { $near: [116.3, 39.9] } }
// ❌ 错误:缺少 type 和 coordinates 封装
{ location: { $near: { $geometry: { coordinates: [116.3, 39.9] } } } }
// ✅ 正确
{ location: { $near: { $geometry: { type: "Point", coordinates: [116.3, 39.9] } } } }
-
$maxDistance单位是米,例如{ $maxDistance: 1000 }表示 1km 内 - 如果用
$geoNear聚合阶段,必须配合near和distanceField,且不能在$match中重复用$near - 注意时区无关——GeoJSON 坐标本身就是 WGS84,MongoDB 内部自动做球面计算
复合索引里能混用2dsphere和其他字段吗?
可以,但有硬性限制:2dsphere 字段必须是复合索引的第一个键。MongoDB 不允许 {"status": 1, "location": "2dsphere"} 这样的定义,会报错 2dsphere index must be the first field。
合理用法是把地理位置放最前,再跟其他高选择性字段:
db.places.createIndex({
"location": "2dsphere",
"category": 1,
"rating": -1
})
- 这种索引支持同时按位置 + 类别 + 评分排序,但
$near查询时,category和rating必须出现在同一$and条件里,否则可能无法命中索引 - 如果查询只用
category不带位置条件,这个复合索引不会被用到,得另建普通索引 - 索引大小增长明显——每个 GeoJSON Point 会额外存储空间编码(GeoHash),大数据集要注意磁盘占用
真正容易被忽略的是坐标顺序和数据清洗:上线前务必用 db.collection.find({ "location.type": { $ne: "Point" } }) 检一遍,漏掉一个非法文档,整个地理查询就可能静默失效。











