必须用geojson point对象存坐标,格式为{"type":"point","coordinates":[lng,lat]},顺序不可颠倒,需建2dsphere索引,查询用$near并设$mindistance/$maxdistance,坐标须为wgs84且在有效范围内。

存坐标必须用 Point 对象,不是随便塞个数组
MongoDB 的 2dsphere 索引只认标准 GeoJSON 结构,直接存 [lng, lat] 数组或两个独立字段(如 lng 和 lat)会导致索引失效、查询报错或返回空结果。
正确写法是把坐标封装成 GeoJSON Point:
{"location": {"type": "Point", "coordinates": [116.3974, 39.9093]}}
注意顺序:必须是 [longitude, latitude],反了会落到非洲海里。
-
coordinates是数字数组,不能是字符串(比如["116.3974", "39.9093"]) -
type字段大小写敏感,必须是"Point",不是"point"或"POINT" - 如果字段名不是
location,建索引时得明确指定该字段路径,比如db.collection.createIndex({"addr.geo": "2dsphere"})
2dsphere 索引要显式创建,不自动生效
即使文档结构符合 GeoJSON,没有建索引,$near、$geoWithin 这类操作也会慢得离谱,甚至触发全表扫描。
建索引命令很简单,但有两个关键点:
- 必须在集合上执行
createIndex(),且类型指定为"2dsphere":db.places.createIndex({"location": "2dsphere"}) - 如果已有数据含非法 GeoJSON(比如
coordinates只有一个数、或type缺失),建索引会失败,得先清理或修复数据 - 索引字段值可以为
null或缺失,MongoDB 会跳过它们——这没问题;但不能是其他非 GeoJSON 类型(如字符串、嵌套对象)
查附近用 $near,别用 $geoNear 聚合阶段除非真需要排序+距离字段
$near 是查询条件,配合 2dsphere 索引能高效返回按距离排序的结果;$geoNear 是聚合管道阶段,开销大,且必须放在 pipeline 开头,还强制要求输出 distance 字段。
日常“找我周边 5km 的咖啡馆”,直接用:
db.places.find({
location: {
$near: {
$geometry: { type: "Point", coordinates: [116.3974, 39.9093] },
$maxDistance: 5000
}
}
})
-
$maxDistance单位是米,不是公里 - 没加
$maxDistance时,默认返回全部匹配文档(可能极慢),务必限制范围 - 如果要用
$geoNear,记得它不支持skip()高效分页,大数据量分页得用游标(find().limit().skip()在$near下也慢,慎用)
坐标精度和范围错误常被忽略:经度 -180~180,纬度 -90~90
超出范围的坐标不会报错,但会被 MongoDB 静默归约(比如经度 200 → -160),导致位置偏移到完全相反的半球。
入库前建议做简单校验:
- 检查
coordinates[0]是否在[-180, 180],coordinates[1]是否在[-90, 90] - 前端传来的 GPS 坐标偶尔带 7 位小数,后端没必要保留——MongoDB 存 double 精度足够,保留过多反而干扰调试
- 国内高德/百度坐标系不是 WGS84,直接存会导致偏移几百米;必须先转成 WGS84 再入库(别信“纠偏算法开源可用”,用成熟 SDK 更稳)
真正麻烦的不是怎么写代码,而是确认坐标来源是否干净、是否已转换、是否被前端无意截断或四舍五入过。这些细节一漏,查出来的“附近”就永远不在你身边。










