Find Restaurants with Geospatial Queries

Overview

MongoDB’s geospatial indexing allows you to efficiently executespatial queries on a collection that contains geospatial shapes andpoints. To showcase the capabilities of geospatial features and comparedifferent approaches, this tutorial will guide you through the processof writing queries for a simple geospatial application.

This tutorial will briefly introduce the concepts of geospatialindexes, and then demonstrate their use with $geoWithin,$geoIntersects, and $nearSphere.

Suppose you are designing a mobile application to help users findrestaurants 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, and
  • Find restaurants within a specified distance of the user using$nearSphere.

This tutorial will use a 2dsphere index to query for this data on sphericalgeometry.

For more information on spherical and flat geometries, seeGeospatial Models.

Distortion

Spherical geometry will appear distorted when visualized on a map due tothe 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 bythe longitude latitude points (0,0), (80,0), (80,80), and(0,80). The following figure depicts the area covered by this region:

Diagram of a square projected onto a sphere.

Searching for Restaurants

Prerequisites

Download the example datasets fromhttps://raw.githubusercontent.com/mongodb/docs-assets/geospatial/neighborhoods.json andhttps://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:

  1. mongoimport <path to restaurants.json> -c=restaurants
  2. mongoimport <path to neighborhoods.json> -c=neighborhoods

A geospatial index, and almostalways improves performance of $geoWithin and $geoIntersectsqueries.

Because this data is geographical, create a 2dsphere index on eachcollection using the mongo shell:

  1. db.restaurants.createIndex({ location: "2dsphere" })
  2. db.neighborhoods.createIndex({ geometry: "2dsphere" })

Exploring the Data

Inspect an entry in the newly-created restaurants collection from within themongo shell:

  1. db.restaurants.findOne()

This query returns a document like the following:

  1. {
  2. location: {
  3. type: "Point",
  4. coordinates: [-73.856077, 40.848447]
  5. },
  6. name: "Morris Park Bake Shop"
  7. }

This restaurant document corresponds to the location shown in the followingfigure:

Map of a single geospatial point.

Because the tutorial uses a 2dsphere index, the geometry data in thelocation field must follow the GeoJSON format.

Now inspect an entry in the neighborhoods collection:

  1. db.neighborhoods.findOne()

This query will return a document like the following:

  1. {
  2. geometry: {
  3. type: "Polygon",
  4. coordinates: [[
  5. [ -73.99, 40.75 ],
  6. ...
  7. [ -73.98, 40.76 ],
  8. [ -73.99, 40.75 ]
  9. ]]
  10. },
  11. name: "Hell's Kitchen"
  12. }

This geometry corresponds to the region depicted in the following figure:

Map of a geospatial polygon.

Find the Current Neighborhood

Assuming the user’s mobile device can give a reasonably accurate location forthe 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:

  1. db.neighborhoods.findOne({ geometry: { $geoIntersects: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] } } } })

This query will return the following result:

  1. {
  2. "_id" : ObjectId("55cb9c666c522cafdb053a68"),
  3. "geometry" : {
  4. "type" : "Polygon",
  5. "coordinates" : [
  6. [
  7. [
  8. -73.93383000695911,
  9. 40.81949109558767
  10. ],
  11. ...
  12. ]
  13. ]
  14. },
  15. "name" : "Central Harlem North-Polo Grounds"
  16. }

Find all Restaurants in the Neighborhood

You can also query to find all restaurants contained in a given neighborhood.Run the following in the mongo shell to find the neighborhoodcontaining the user, and then count the restaurants within that neighborhood:

  1. var neighborhood = db.neighborhoods.findOne( { geometry: { $geoIntersects: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] } } } } )
  2. db.restaurants.find( { location: { $geoWithin: { $geometry: neighborhood.geometry } } } ).count()

This query will tell you that there are 127 restaurants in the requestedneighborhood, visualized in the following figure:

Map of all restaurants in a geospatial polygon.

Find Restaurants within a Distance

To find restaurants within a specified distance of a point, you canuse either $geoWithin with $centerSphere to return resultsin unsorted order, or nearSphere with $maxDistance if you needresults sorted by distance.

Unsorted with $geoWithin

To find restaurants within a circular region, use $geoWithin with$centerSphere. $centerSphere is a MongoDB-specific syntax todenote a circular region by specifying the center and the radius in radians.

$geoWithin does not return the documents in any specific order, so itmay show the user the furthest documents first.

The following will find all restaurants within five miles of the user:

  1. db.restaurants.find({ location:
  2. { $geoWithin:
  3. { $centerSphere: [ [ -73.93414657, 40.82302903 ], 5 / 3963.2 ] } } })

$centerSphere’s second argument accepts the radius in radians, so youmust divide it by the radius of the earth in miles. SeeCalculate Distance Using Spherical Geometryfor more information on converting between distance units.

Sorted with $nearSphere

You may also use $nearSphere and specify a $maxDistance termin meters. This will return all restaurants within five miles of the user insorted order from nearest to farthest:

  1. var METERS_PER_MILE = 1609.34
  2. db.restaurants.find({ location: { $nearSphere: { $geometry: { type: "Point", coordinates: [ -73.93414657, 40.82302903 ] }, $maxDistance: 5 * METERS_PER_MILE } } })