Find Restaurants with Geospatial Queries

On this page

Overview

MongoDB’sgeospatialindexing allows you to efficiently execute spatial queries on a collection that contains geospatial shapes and points. This tutorial will briefly introduce the concepts of geospatial indexes, and then demonstrate their use with$geoWithin,$geoIntersects, andgeoNear.

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.

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
    , and
  • Find restaurants within a specified distance of the user using
    $nearSphere
    .

This tutorial will use a2dsphereindex to query for this data on spherical geometry.

For more information on spherical and flat geometries, seeGeospatial 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:

通过地理位置查找餐厅 - 图1

Searching for Restaurants

Prerequisites

Download the example datasets fromhttps://raw.githubusercontent.com/mongodb/docs-assets/geospatial/neighborhoods.jsonandhttps://raw.githubusercontent.com/mongodb/docs-assets/geospatial/restaurants.json. These contain the collectionsrestaurantsandneighborhoodsrespectively.

After downloading the datasets, import them into the database:

  1. mongoimport
  2. <
  3. path
  4. to
  5. restaurants
  6. .
  7. json
  8. >
  9. -
  10. c
  11. restaurants
  12. mongoimport
  13. <
  14. path
  15. to
  16. neighborhoods
  17. .
  18. json
  19. >
  20. -
  21. c
  22. neighborhoods

ThegeoNearcommand requires a geospatial index, and almost always improves performance of$geoWithinand$geoIntersectsqueries.

Because this data is geographical, create a2dsphereindex on each collection using themongoshell:

  1. db
  2. .
  3. restaurants
  4. .
  5. createIndex
  6. ({
  7. location
  8. :
  9. "2dsphere"
  10. })
  11. db
  12. .
  13. neighborhoods
  14. .
  15. createIndex
  16. ({
  17. geometry
  18. :
  19. "2dsphere"
  20. })

Exploring the Data

Inspect an entry in the newly-createdrestaurantscollection from within themongoshell:

  1. db
  2. .
  3. restaurants
  4. .
  5. findOne
  6. ()

This query returns a document like the following:

  1. {
  2. location
  3. :
  4. {
  5. type
  6. :
  7. "Point"
  8. ,
  9. coordinates
  10. :
  11. [
  12. -
  13. 73.856077
  14. ,
  15. 40.848447
  16. ]
  17. },
  18. name
  19. :
  20. "Morris Park Bake Shop"
  21. }

This restaurant document corresponds to the location shown in the following figure:

通过地理位置查找餐厅 - 图2

Because the tutorial uses a2dsphereindex, the geometry data in thelocationfield must follow theGeoJSON format.

Now inspect an entry in theneighborhoodscollection:

  1. db
  2. .
  3. neighborhoods
  4. .
  5. findOne
  6. ()

This query will return a document like the following:

  1. {
  2. geometry
  3. :
  4. {
  5. type
  6. :
  7. "Polygon"
  8. ,
  9. coordinates
  10. :
  11. [[
  12. [
  13. -
  14. 73.99
  15. ,
  16. 40.75
  17. ],
  18. ...
  19. [
  20. -
  21. 73.98
  22. ,
  23. 40.76
  24. ],
  25. [
  26. -
  27. 73.99
  28. ,
  29. 40.75
  30. ]
  31. ]]
  32. },
  33. name
  34. :
  35. "Hell's Kitchen"
  36. }

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

通过地理位置查找餐厅 - 图3

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$geometryfield inGeoJSONformat:

  1. db
  2. .
  3. neighborhoods
  4. .
  5. findOne
  6. ({
  7. geometry
  8. :
  9. {
  10. $geoIntersects
  11. :
  12. {
  13. $geometry
  14. :
  15. {
  16. type
  17. :
  18. "Point"
  19. ,
  20. coordinates
  21. :
  22. [
  23. -
  24. 73.93414657
  25. ,
  26. 40.82302903
  27. ]
  28. }
  29. }
  30. }
  31. })

This query will return the following result:

  1. {
  2. "_id"
  3. :
  4. ObjectId
  5. (
  6. "55cb9c666c522cafdb053a68"
  7. ),
  8. "geometry"
  9. :
  10. {
  11. "type"
  12. :
  13. "Polygon"
  14. ,
  15. "coordinates"
  16. :
  17. [
  18. [
  19. [
  20. -
  21. 73.93383000695911
  22. ,
  23. 40.81949109558767
  24. ],
  25. ...
  26. ]
  27. ]
  28. },
  29. "name"
  30. :
  31. "Central Harlem North-Polo Grounds"
  32. }

Find all Restaurants in the Neighborhood

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

  1. var
  2. neighborhood
  3. =
  4. db
  5. .
  6. neighborhoods
  7. .
  8. findOne
  9. (
  10. {
  11. geometry
  12. :
  13. {
  14. $geoIntersects
  15. :
  16. {
  17. $geometry
  18. :
  19. {
  20. type
  21. :
  22. "Point"
  23. ,
  24. coordinates
  25. :
  26. [
  27. -
  28. 73.93414657
  29. ,
  30. 40.82302903
  31. ]
  32. }
  33. }
  34. }
  35. }
  36. )
  37. db
  38. .
  39. restaurants
  40. .
  41. find
  42. (
  43. {
  44. location
  45. :
  46. {
  47. $geoWithin
  48. :
  49. {
  50. $geometry
  51. :
  52. neighborhood
  53. .
  54. geometry
  55. }
  56. }
  57. }
  58. ).
  59. count
  60. ()

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

通过地理位置查找餐厅 - 图4

Find Restaurants within a Distance

To find restaurants within a specified distance of a point, you can use either$geoWithinwith$centerSphereto return results in unsorted order, ornearSpherewith$maxDistanceif you need results sorted by distance.

Unsorted with$geoWithin

To find restaurants within a circular region, use$geoWithinwith$centerSphere.$centerSphereis a MongoDB-specific syntax to denote a circular region by specifying the center and the radius in radians.

$geoWithindoes 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:

  1. db
  2. .
  3. restaurants
  4. .
  5. find
  6. ({
  7. location
  8. :
  9. {
  10. $geoWithin
  11. :
  12. {
  13. $centerSphere
  14. :
  15. [
  16. [
  17. -
  18. 73.93414657
  19. ,
  20. 40.82302903
  21. ],
  22. 5
  23. /
  24. 3963.2
  25. ]
  26. }
  27. }
  28. })

$centerSphere’s second argument accepts the radius in radians, so you must 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$nearSphereand specify a$maxDistanceterm in meters. This will return all restaurants within five miles of the user in sorted order from nearest to farthest:

  1. var
  2. METERS_PER_MILE
  3. =
  4. 1609.34
  5. db
  6. .
  7. restaurants
  8. .
  9. find
  10. ({
  11. location
  12. :
  13. {
  14. $nearSphere
  15. :
  16. {
  17. $geometry
  18. :
  19. {
  20. type
  21. :
  22. "Point"
  23. ,
  24. coordinates
  25. :
  26. [
  27. -
  28. 73.93414657
  29. ,
  30. 40.82302903
  31. ]
  32. },
  33. $maxDistance
  34. :
  35. 5
  36. *
  37. METERS_PER_MILE
  38. }
  39. }
  40. })