通过地理空间查询查找餐厅
概述
MongoDB 的地理空间索引可让您对包含地理空间形状和点的集合高效地执行空间查询。为了展示地理空间特征的功能并比较不同的方法,本教程将指导您完成为简单的地理空间应用程序编写查询的过程。
本教程将简要介绍地理空间索引的概念,然后演示其与 $geoWithin
、$geoIntersects
和 $nearSphere
的用法。
假设您正在设计一个移动应用程序来帮助用户查找纽约市的餐厅。该应用程序必须:
使用
$geoIntersects
确定用户的当前社区,使用
$geoWithin
显示该社区中的餐馆数量,以及使用
$nearSphere
查找用户指定距离内的餐厅。
本教程将使用 2dsphere
索引来查询有关球面几何的数据。
有关球面和平面几何图形的更多信息,请参阅地理空间模型。
失真
由于将三维球体(例如地球)投影到平面的特质,球面几何图形在地图上进行可视化时会出现变形。
以由经纬度点 (0,0)
、(80,0)
、(80,80)
和 (0,80)
所定义的球面正方形的规范为例。下图描绘了该地区所涵盖的范围:
![投影到球体上的正方形示意图。](/zh-cn/docs/manual/static/3dc97d5ac62601b63ead0ecdd6118792/86600/geospatial-spherical-square.webp)
搜索餐厅
先决条件
通过以下链接下载示例数据集: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
为每个集合创建一个 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" }
该餐厅文档对应下图中所示的位置:
![单个地理空间点的地图。](/zh-cn/docs/manual/static/727d307582367d1714511b088bcb0d5d/d5a20/geospatial-single-point.webp)
由于本教程使用 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" }
该几何图形对应为下图所示的区域:
![地理空间多边形的地图。](/zh-cn/docs/manual/static/964f6285c2091eba2fceb444db5d8299/0bd87/geospatial-polygon-hells-kitchen.webp)
查找当前邻近区域
假设用户的移动设备可为用户提供一个相当准确的位置,使用 $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 家餐厅,如下图所示:
![地理空间多边形中所有餐馆的地图。](/zh-cn/docs/manual/static/759209df93ce3d780bdd71a9462151e9/ec7dc/geospatial-all-restaurants.webp)
查找附近的餐馆
要查找距离某一点指定距离内的餐厅,可使用 $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 } } })