Find Restaurants with Geospatial Queries
On this page
Overview
MongoDB's geospatial indexing allows you to efficiently execute spatial queries on a collection that contains geospatial shapes and points. To showcase the capabilities of geospatial features and compare different approaches, this tutorial will guide you through the process of writing queries for a simple geospatial application.
This tutorial will briefly introduce the concepts of geospatial
indexes, and then demonstrate their use with $geoWithin
,
$geoIntersects
, and $nearSphere
.
Suppose you are designing a mobile application to help users find restaurants in New York City. The application must:
Determine the user's current neighborhood using
$geoIntersects
,Show the number of restaurants in that neighborhood using
$geoWithin
, andFind restaurants within a specified distance of the user using
$nearSphere
.
This tutorial will use a 2dsphere
index to query for this data on spherical
geometry.
For more information on spherical and flat geometries, see Geospatial Models.
Distortion
Spherical geometry will appear distorted when visualized on a map due to the nature of projecting a three dimensional sphere, such as the earth, onto a flat plane.
For example, take the specification of the spherical square defined by
the longitude latitude points (0,0)
, (80,0)
, (80,80)
, and
(0,80)
. The following figure depicts the area covered by this region:
Searching for Restaurants
Prerequisites
Download the example datasets from
https://raw.githubusercontent.com/mongodb/docs-assets/geospatial/neighborhoods.json and
https://raw.githubusercontent.com/mongodb/docs-assets/geospatial/restaurants.json.
These contain the collections restaurants
and neighborhoods
respectively.
After downloading the datasets, import them into the database:
mongoimport <path to restaurants.json> -c=restaurants mongoimport <path to neighborhoods.json> -c=neighborhoods
A geospatial index, and almost
always improves performance of $geoWithin
and $geoIntersects
queries.
Because this data is geographical, create a 2dsphere
index on each
collection using mongosh
:
db.restaurants.createIndex({ location: "2dsphere" }) db.neighborhoods.createIndex({ geometry: "2dsphere" })
Exploring the Data
Inspect an entry in the newly-created restaurants
collection in
mongosh
:
db.restaurants.findOne()
This query returns a document like the following:
{ location: { type: "Point", coordinates: [-73.856077, 40.848447] }, name: "Morris Park Bake Shop" }
This restaurant document corresponds to the location shown in the following figure:
Because the tutorial uses a 2dsphere
index, the geometry data in the
location
field must follow the GeoJSON format.
Now inspect an entry in the neighborhoods
collection:
db.neighborhoods.findOne()
This query will return a document like the following:
{ geometry: { type: "Polygon", coordinates: [[ [ -73.99, 40.75 ], ... [ -73.98, 40.76 ], [ -73.99, 40.75 ] ]] }, name: "Hell's Kitchen" }
This geometry corresponds to the region depicted in the following figure:
Find the Current Neighborhood
Assuming the user's mobile device can give a reasonably accurate location for
the user, it is simple to find the user's current neighborhood with
$geoIntersects
.
Suppose the user is located at -73.93414657 longitude and 40.82302903 latitude.
To find the current neighborhood, you will specify a point using the special
$geometry
field in GeoJSON format:
db.neighborhoods.findOne({ geometry: { $geoIntersects: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] } } } })
This query will return the following result:
{ "_id" : ObjectId("55cb9c666c522cafdb053a68"), "geometry" : { "type" : "Polygon", "coordinates" : [ [ [ -73.93383000695911, 40.81949109558767 ], ... ] ] }, "name" : "Central Harlem North-Polo Grounds" }
Find all Restaurants in the Neighborhood
You can also query to find all restaurants contained in a given neighborhood.
Run the following in mongosh
to find the neighborhood
containing the user, and then count the restaurants within that neighborhood:
var neighborhood = db.neighborhoods.findOne( { geometry: { $geoIntersects: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] } } } } ) db.restaurants.find( { location: { $geoWithin: { $geometry: neighborhood.geometry } } } ).count()
This query will tell you that there are 127 restaurants in the requested neighborhood, visualized in the following figure:
Find Restaurants within a Distance
To find restaurants within a specified distance of a point, you can
use either $geoWithin
with $centerSphere
to return results
in unsorted order, or $nearSphere
with $maxDistance
if you need
results sorted by distance.
Unsorted with $geoWithin
To find restaurants within a circular region, use $geoWithin
with
$centerSphere
. $centerSphere
is a MongoDB-specific syntax to
denote a circular region by specifying the center and the radius in radians.
$geoWithin
does not return the documents in any specific order, so it
may show the user the furthest documents first.
The following will find all restaurants within five miles of the user:
db.restaurants.find({ location: { $geoWithin: { $centerSphere: [ [ -73.93414657, 40.82302903 ], 5 / 3963.2 ] } } })
$centerSphere
's second argument accepts the radius in radians, so you
must divide it by the radius of the earth in miles. See
Convert Distance to Radians for Spherical Operators
for more information on converting between distance units.
Sorted with $nearSphere
You may also use $nearSphere
and specify a $maxDistance
term
in meters. This will return all restaurants within five miles of the user in
sorted order from nearest to farthest:
var METERS_PER_MILE = 1609.34 db.restaurants.find({ location: { $nearSphere: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] }, $maxDistance: 5 * METERS_PER_MILE } } })