Query locations within a radius in MongoDB

It's a 3 step process.

  • Step 1) Embed a GeoJSON Point within your documents.
  • Step 2) Index the path to the points, using 2dsphere.
  • Step 3) Query the points in the documents using $geoWithin and $centerSphere.

To perform geospatial queries, you need to change the document structure to match a GeoJSON Point. Which looks like this.

loc : {
    type : "Point",
    coordinates : [lng, lat]
}

Sample code for translating your collection to the Point format.

// sample setup code.
// use test; 
// db.positions.drop();
// db.positions.insert({
    // pos : {
        // lat : 0,
        // lon : 30
    // }
// });
db.positions.find().forEach(function (doc) {
    var point = {
        _id : doc._id,
        loc : {
            type : "Point",
            coordinates : [doc.pos.lon, doc.pos.lat]
        }
    };
    db.positions.update(doc, point);
});
db.positions.find().pretty();

Afterwards, you can use $geoWithin and $near operators in your queries like the example below.

Example

Setup

var createLandmarkDoc = function (name, lng, lat) {
    return {
        name : name,
        loc : {
            type : "Point",
            coordinates : [lng, lat]
        }
    };
};
var addNewLandmark = function(name, lng, lat){
    db.landmarks.insert(createLandmarkDoc(name, lng, lat));
};

db.landmarks.drop();
// Step 1: Add points.
addNewLandmark("Washington DC", 38.8993487, -77.0145665);
addNewLandmark("White House", 38.9024593, -77.0388266);
addNewLandmark("Library of Congress", 38.888684, -77.0047189);
addNewLandmark("Patuxent Research Refuge", 39.0391718, -76.8216182);
addNewLandmark("The Pentagon", 38.871857, -77.056267);
addNewLandmark("Massachusetts Institute of Technology", 42.360091, -71.09416);

// Step 2: Create index
db.landmarks.ensureIndex({
    loc : "2dsphere"
});

Query: Find landmarks within 5 miles.

var milesToRadian = function(miles){
    var earthRadiusInMiles = 3959;
    return miles / earthRadiusInMiles;
};
var landmark = db.landmarks.findOne({name: "Washington DC"});
var query = {
    "loc" : {
        $geoWithin : {
            $centerSphere : [landmark.loc.coordinates, milesToRadian(5) ]
        }
    }
};
// Step 3: Query points.
db.landmarks.find(query).pretty();

Output

{
        "_id" : ObjectId("540e70c96033ed0d2d9694fa"),
        "name" : "Washington DC",
        "loc" : {
                "type" : "Point",
                "coordinates" : [
                        38.8993487,
                        -77.0145665
                ]
        }
}
{
        "_id" : ObjectId("540e70c96033ed0d2d9694fc"),
        "name" : "Library of Congress",
        "loc" : {
                "type" : "Point",
                "coordinates" : [
                        38.888684,
                        -77.0047189
                ]
        }
}
{
        "_id" : ObjectId("540e70c96033ed0d2d9694fb"),
        "name" : "White House",
        "loc" : {
                "type" : "Point",
                "coordinates" : [
                        38.9024593,
                        -77.0388266
                ]
        }
}
{
        "_id" : ObjectId("540e70c96033ed0d2d9694fe"),
        "name" : "The Pentagon",
        "loc" : {
                "type" : "Point",
                "coordinates" : [
                        38.871857,
                        -77.056267
                ]
        }
}

More Info:

  • 2dsphere
  • 2dsphere Index
  • $centerSphere
  • Geospatial Query Operators

Step by step radial location querying with MongoDB

MongoDB is great for computing geolocation using geospatial data

Basicaly you use `$geoWithin:` with `$centerSphere:` on collection with **2dsphere** index. But lets go step by step.

Scenario: Lets pretend a user is browsing hotels in his area... He want to display hotels that are in 50 mile range from his current location and your database app is full of hotels data. You would probably would use some MAP API to display them on the map (if that's what you after be sure to check out links at the bottom) but for this example we just just want to fetch hotels data with in 50 miles range from hotels location in your MongoDB database.

First create test document collection 'hotels'

* You might want to edit coordinates to those matching your nearby locations if you want this code to work without any change in radius distance.

// Your 'hotels' collection could look something like this:
db.hotels.insertMany([
  {
    'name': 'Golebiewski',
    'address': 'Poland, Mikolajki',
    'location': { type: "Point", coordinates: [ 40, 5.000001 ] },
    'tel': '000 000 000'
  },
  {
    'name': 'Baltazar',
    'address': 'Poland, Pultusk 36',
    'location': { type: "Point", coordinates: [ 40, 5.000002 ] },
    'tel': '000 000 000'
  },
  {
    'name': 'Zamek',
    'address': 'Poland, Pultusk',
    'location': { type: "Point", coordinates: [ 40, 5.000003 ] },
    'tel': '000 000 000'
  },
])

Then index location with 2dsphere

db.places.createIndex( { location : "2dsphere" } )

Now query based on miles range and your location.

Get your tools ready first:
// custom function - convert miles to radian
let milesToRadian = function(miles){
    var earthRadiusInMiles = 3963;
    return miles / earthRadiusInMiles;
};

// or custom function - convert km to radian
let kmToRadian = function(miles){
    var earthRadiusInMiles = 6378;
    return miles / earthRadiusInMiles;
};


// custom function - returns array with current user geolocation points - [latitude, longitude]
function getUserLocation() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function (position) {
      let geoPoints = [position.coords.latitude, position.coords.longitude];
      console.log(geoPoints);
      return geoPoints;
    });
  } else { 
    console.log("Geolocation is not supported by this browser.");
  }
}

Now use your new tools to create query for MongoDB geospatial data.

var query = {'location' : {$geoWithin: { $centerSphere:  getUserLocation(), milesToRadian(50) ]}}}

// MongoDB example query could look something like:
// {'location': {$geoWithin: { $centerSphere: [ [ 40.000000001, 5.000000001 ], 0.012616704516780217 ]}}}

db.hotels.find(query);

MongoDB returns list of documents (hotels) with in 50 miles range from user location. Just remember that your document need to implement GeoJSON structure for storing location and it needs to be index 2dsphere. Result will be sorted by distance from user - ASC.

Output

{ "_id" : ObjectId("5d3130f810050424d26836d6"), "name" : "Golebiewski", "address" : "Poland, Mikolajki", "location" : { "type" : "Point", "coordinates" : [ 40, 5.000001 ] }, "tel" : "000 000 000" }
{ "_id" : ObjectId("5d3130f810050424d26836d7"), "name" : "Baltazar", "address" : "Poland, Pultusk 36", "location" : { "type" : "Point", "coordinates" : [ 40, 5.000002 ] }, "tel" : "000 000 000" }
{ "_id" : ObjectId("5d3130f810050424d26836d8"), "name" : "Zamek", "address" : "Poland, Pultusk", "location" : { "type" : "Point", "coordinates" : [ 40, 5.000003 ] }, "tel" : "000 000 000" }

Watch / Read more

MongoDB - geospatial queries: https://docs.mongodb.com/manual/geospatial-queries/ https://www.youtube.com/watch?v=3xP-gzh2ck0

Go further!

Get your data on to the map. Here you have some really cool tutorials for that.

Open Street Map with MapBox API: https://docs.mapbox.com/help/tutorials/building-a-store-locator/

Google Maps with Google JavaScript API: https://developers.google.com/maps/documentation/javascript/marker-clustering

If you want to convert address to geocode points, you can use Nominatim gui or Nominatim API

For example search for "Poland Pultusk" in browser or request a json response by adding to a string parameter &format=json

https://nominatim.openstreetmap.org/search.php?q=Poland+Pultusk https://nominatim.openstreetmap.org/search.php?q=Poland+Pultusk&format=json

Ps. MapBox is free up to 50k loads per month then $5 / 1000 API calls. Google gives you $200 MONTHLY CREDIT FREE USAGE which comes to 28k free API calls then 7$ / 1000 api calls. Pick your weapon and have fun :>.

Tools: MongoDB Compass (*not the community edition) https://www.mongodb.com/blog/post/visualizing-your-data-with-mongodb-compass