MongoDB CRUD操作:地理位置应用——通过地理空间查询查找餐厅
文章目录
- MongoDB CRUD操作:地理位置应用——通过地理空间查询查找餐厅
- 地图的扭曲
- 搜索餐厅
- 浏览数据
- 查找当前邻居
- 查找附近所有餐厅
- 查找一定距离内的餐厅
- 使用`$geoWithin`,不排序
- 使用`$nearSphere`,排序
MongoDB通过对地理空间建立索引,可以对包含地理空间形状和点的集合上高效地执行空间查询,这边文章介绍了地理空间索引的的基本概念,介绍了地理空间查询的常用方法,演示了
$geoWithin
、
$geoIntersects
和
$nearSphere
的使用。
这里以一个App为例,介绍如何来帮助用户查找纽约市的餐厅。App完成的功能有:
- 使用
$geoIntersects
查询用户的邻居 - 使用
$geoWithin
显示社区的餐厅数量 - 使用
$nearSphere
查找用户指定距离内的餐厅
本文将使用2dsphere索引来查询有关球面几何的数据。
地图的扭曲
由于三维球体(例如地球)投影到平面上的原因,在地图上可视化时,球面几何形状会出现一定程度的扭曲。例如,采用由经纬度点 (0,0)、(80,0)、(80,80)和(0,80) 定义的球形正方形的规范。下图描绘了该区域所覆盖的区域:
搜索餐厅
在进行后续的操作前,需要先准备一些数据,需要先下载邻居neighborhoods和餐厅restaurants的集合,并使用下面的命令将其导入数据库:
mongoimport restaurants.json -c=restaurants
mongoimport neighborhoods.json -c=neighborhoods
地理空间索引能在很大程度上提升$geoWithin
和$geoIntersects
的查询性能,下面使用mongosh在对两个集合2dsphere索引:
db.restaurants.createIndex({ location: "2dsphere" })
db.neighborhoods.createIndex({ geometry: "2dsphere" })
浏览数据
查看mongosh中新创建的restaurants
集合中的条目:
db.restaurants.findOne()
此查询返回下面的文档:
{
location: {
type: "Point",
coordinates: [-73.856077, 40.848447]
},
name: "Morris Park Bake Shop"
}
餐厅对应的位置如下图所示:
由于使用的是2dsphere索引,因此位置字段中的几何体数据必须遵循GeoJSON格式。
查看邻居neighborhoods
集合中的一个条目:
db.neighborhoods.findOne()
查询将返回下面的文档:
{
geometry: {
type: "Polygon",
coordinates: [[
[ -73.99, 40.75 ],
...
[ -73.98, 40.76 ],
[ -73.99, 40.75 ]
]]
},
name: "Hell's Kitchen"
}
数据对应下面的图形显示的区域:
查找当前邻居
如果用户的移动设备可以为用户提供准确的位置,那么使用$geoIntersects
查找用户当前的邻居就很简单,
假设用户位于经度-73.93414657
、纬度40.82302903
,要查找当前的邻居,需要使用GeoJSON格式的$geometry
字段指定一个点:
db.neighborhoods.findOne({ geometry: { $geoIntersects: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] } } } })
该查询返回以下结果:
{
"_id" : ObjectId("55cb9c666c522cafdb053a68"),
"geometry" : {
"type" : "Polygon",
"coordinates" : [
[
[
-73.93383000695911,
40.81949109558767
],
...
]
]
},
"name" : "Central Harlem North-Polo Grounds"
}
查找附近所有餐厅
要查询给定区域中包含的所有餐厅,可以在mongosh中运行以下命令,可查询出所有餐厅,并计算该社区内的餐厅数量:
var neighborhood = db.neighborhoods.findOne( { geometry: { $geoIntersects: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] } } } } )
db.restaurants.find( { location: { $geoWithin: { $geometry: neighborhood.geometry } } } ).count()
查询显示,在所请求的社区内有127家餐厅,如下图所示:
查找一定距离内的餐厅
要查找某个位置指定距离内的餐厅,可以使用$geoWithin
和$centerSphere
返回无序结果,或者使用$nearSphere
和$maxDistance
(如需要按距离排序)。
使用$geoWithin
,不排序
要查找圆形区域内的餐厅,可使用$geoWithin
和$centerSphere
。$centerSphere
是MongoDB特有的语法,通过指定中心和半径(以弧度为单位)来表示圆形区域。
$geoWithin
不会按任何特定顺序返回文档,因此它可能会先向用户显示最远的文档。下面的示例查找距离用户5英里以内的所有餐厅:
db.restaurants.find({ location:
{ $geoWithin:
{ $centerSphere: [ [ -73.93414657, 40.82302903 ], 5 / 3963.2 ] } } })
$centerSphere
的第二个参数接受以弧度为单位的半径,因此必须除以以英里为单位的地球半径。
使用$nearSphere
,排序
也可以使用$nearSphere
,并指定以米为单位的$maxDistance
(最大距离),将按照从最近到最远的排序方式,返回距离用户5英里范围内的所有餐厅:
var METERS_PER_MILE = 1609.34
db.restaurants.find({ location: { $nearSphere: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] }, $maxDistance: 5 * METERS_PER_MILE } } })