Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Query locations within a radius in MongoDB

Tags:

If I have a document in the form of:

{
    pos:  { lat: 0, lon: 30 }
}

in the collection users (please pretend these are real lat / lon :-) )

What is the correct way to get all values within a certain radius of say: 50 miles?

like image 398
Paul Avatar asked Sep 08 '14 22:09

Paul


People also ask

Does MongoDB support range query search?

Explanation: MongoDB supports search by field, range queries, regular expression searches.

How do I query multiple values in MongoDB?

MongoDB provides the find() that is used to find multiple values or documents from the collection. The find() method returns a cursor of the result set and prints all the documents. To find the multiple values, we can use the aggregation operations that are provided by MongoDB itself.

How do I find a particular collection in MongoDB?

Find() Method. In MongoDB, find() method is used to select documents in a collection and return a cursor to the selected documents. Cursor means a pointer that points to a document, when we use find() method it returns a pointer on the selected documents and returns one by one.


1 Answers

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
like image 172
Larry Battle Avatar answered Oct 22 '22 23:10

Larry Battle