地理空间查询的两类写法
MongoDB 的地理查询围绕两个索引类型展开:2dsphere 处理球面距离,支持 GeoJSON(点、线、多边形);2d 处理平面坐标,适合老式地图数据。查询操作符按意图分成两类:$near 按距离由近到远返回,$geoWithin 判断点是否落在指定区域内(不排序)。选之前先确认坐标存储格式与是否已有对应索引。
基本信息
| 索引类型 | 2dsphere(球面,GeoJSON)/ 2d(平面,旧式坐标对) |
|---|---|
| $near | 距离排序,可配 $maxDistance、$minDistance,必须走地理索引 |
| $geoWithin | 区域内查询(圆、矩形、多边形),结果不排序 |
| $geoIntersects | 判断几何对象是否与目标相交,适合路线与围栏 |
| 聚合写法 | $geoNear 必须是聚合管道第一阶段,返回结果带距离字段 |
| 单位提醒 | GeoJSON 坐标下 $maxDistance 以米为单位;旧式坐标对单位不同,建议统一用 GeoJSON |
典型使用场景
- 附近的门店与网点:按距离排序并限制半径,$near 最直接
- 配送范围判定:用多边形或圆做 $geoWithin,判断地址是否在服务区内
- 电子围栏与路线匹配:路段与围栏相交判断用 $geoIntersects
- 聚合带距离:需要在结果里带上距离值并与其他条件组合时用 $geoNear
- 围栏内计数统计:区域内统计常配合聚合在数据库侧完成
建索引与查询
建索引与查询
// 坐标必须以 GeoJSON 形式存储,注意顺序是 [经度, 纬度]
db.stores.insertOne({ name: "门店A", loc: { type: "Point", coordinates: [121.47, 31.23] } })
db.stores.createIndex({ loc: "2dsphere" })
// 附近的点:按距离排序,限制 1 公里内
db.stores.find({
loc: { $near: {
$geometry: { type: "Point", coordinates: [121.47, 31.23] },
$maxDistance: 1000
} }
})
// 区域内的点:多边形(首尾点需闭合)
db.stores.find({ loc: { $geoWithin: { $geometry: {
type: "Polygon",
coordinates: [[[121.4,31.2],[121.5,31.2],[121.5,31.3],[121.4,31.3],[121.4,31.2]]]
} } } })
// 聚合形式:返回结果里带距离字段,便于排序展示
// db.stores.aggregate([{ $geoNear: { near: { type: "Point", coordinates: [121.47, 31.23] },
// distanceField: "dist", maxDistance: 1000, spherical: true } }])操作符对照
| 操作符 | 作用 | 使用限制 |
|---|---|---|
| $near | 按距离由近到远排序 | 必须有地理索引;不能与 $or 等部分操作符组合 |
| $geoWithin | 点落在区域内,不排序 | 结果无序,需要排序要在应用层处理 |
| $geoIntersects | 几何相交判断 | 需明确坐标参考系与几何类型 |
| $maxDistance / $minDistance | 限制距离范围 | 单位随坐标格式变化,GeoJSON 用米 |
| $geoNear(聚合) | 带距离的输出与更多筛选能力 | 必须是管道第一阶段 |
GeoJSON 的坐标顺序是 [经度, 纬度],与常见的「先纬度后经度」书写习惯相反;写反不会报错,只会得到相隔很远的结果,调试时先核对这一条。
缺少地理索引时 $near 会直接报错,$geoWithin 则退化为低效扫描:上线前确认索引存在,并注意球面索引与平面索引的选择会影响距离精度。