使用地理空间查询查找餐厅
概述
MongoDB的地理空间索引允许您在包含地理空间形状和点的集合上高效执行空间查询。为了展示地理空间特性的功能并比较不同的方法,本教程将指导您编写一个简单的地理空间应用程序的查询过程。
本教程将简要介绍地理空间索引的概念,然后使用$geoWithin
、$geoIntersects
和$nearSphere
来演示其使用。
假设您正在设计一个移动应用程序,帮助用户在纽约市找到餐厅。该应用程序必须
使用
$geoIntersects
确定用户的当前社区使用
$geoWithin
显示该社区中的餐厅数量使用
$nearSphere
找到用户指定距离内的餐厅
本教程将使用2dsphere
索引在球形几何上查询这些数据。
有关球形和平面几何的更多信息,请参阅地理空间模型。
失真
由于将三维球体(如地球)投影到平面上的性质,球面几何在地图上显示时会失真。
例如,考虑由经纬度点定义的球面正方形规范 (0,0)
、(80,0)
、(80,80)
和 (0,80)
。以下图显示了该区域覆盖的区域

搜索餐厅
先决条件
从以下位置下载示例数据集https://raw.githubusercontent.com/mongodb/docs-assets/geospatial/neighborhoods.json 以及 https://raw.githubusercontent.com/mongodb/docs-assets/geospatial/restaurants.json。这些包含分别名为 restaurants
和 neighborhoods
的集合。
下载数据集后,将其导入数据库
mongoimport <path to restaurants.json> -c=restaurants mongoimport <path to neighborhoods.json> -c=neighborhoods
地理空间索引,几乎总是可以提高 $geoWithin
和 $geoIntersects
查询的性能。
由于这些数据是地理数据,请使用 mongosh
:
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
索引,所以 location
字段中的几何数据必须遵循 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。要找到当前社区,您将使用特殊的 $geometry
字段在 GeoJSON 格式下指定一个点
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
不会按照任何特定顺序返回文档,因此可能会先显示用户距离较远的文档。
以下操作将找到用户周围五英里内的所有餐厅
db.restaurants.find({ location: { $geoWithin: { $centerSphere: [ [ -73.93414657, 40.82302903 ], 5 / 3963.2 ] } } })
$centerSphere
的第二个参数接受弧度制的半径,因此您必须将其除以地球的半径(英里)。有关将距离单位转换为弧度的更多信息,请参阅将距离转换为球面运算符的弧度。
使用$nearSphere
排序
您还可以使用$nearSphere
并指定一个$maxDistance
(以米为单位)的术语。这将按从近到远的顺序返回用户周围五英里内的所有餐厅
var METERS_PER_MILE = 1609.34 db.restaurants.find({ location: { $nearSphere: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] }, $maxDistance: 5 * METERS_PER_MILE } } })