Reputation: 765
I have an api for geo data realised with node.js mongodb and mongoose. I want to query my data with two criteria.
First i use geoNear to get all locations in a given radius which works fine. Second i want to filter the locations further by its type.
This is my schema:
var GeoLocationSchema = new Schema({
name: String,
type: String,
loc: {
type: {
type: String,
required: true,
enum: ["Point", "LineString", "Polygon"],
default: "Point"
},
coordinates: [Number]
}
});
// ensure the geo location uses 2dsphere
GeoLocationSchema.index({ loc : "2dsphere" });
and this is my geoNear query:
router.route("/locations/near/:lng/:lat/:type/:max")
// get locations near the get params
.get(function(req, res) {
// setup geoJson for query
var geoJson = {};
geoJson.type = "Point";
geoJson.coordinates = [parseFloat(req.params.lng), parseFloat(req.params.lat)];
// setup options for query
var options = {};
options.spherical = true;
options.maxDistance = parseInt(req.params.max)/6370000;
// query db with mongoose geoNear wrapper
GeoLocation.geoNear(geoJson, options, function (err, results, stats) {
if (err)
res.send(err);
res.json(results);
});
});
Is it somehow possible to combine the geoNear query with a query for a specific type of location or can i filter the result somehow?
Thanks in advance.
Upvotes: 2
Views: 4944
Reputation: 150
You can just pass the additional query as an option to the mongoose geoNear function like so:
// setup geoJson for query
var geoJson = {};
geoJson.type = "Point";
geoJson.coordinates = [parseFloat(req.params.lng), parseFloat(req.params.lat)];
// setup options for query
var options = {};
options.spherical = true;
options.maxDistance = parseInt(req.params.max)/6370000;
// you can put any query here to further refine the result.
options.query = { "loc.type": "Point" };
// query db with mongoose geoNear wrapper
GeoLocation.geoNear(geoJson, options, function (err, results, stats) {
if (err)
res.send(err);
res.json(results);
});
Upvotes: 2
Reputation: 151170
As long as your MongoDB server is recent enough, being a version of 2.6 or greater then this functionality has actually been moved to the general query engine. The mongoose method here wraps the .runCommand()
form which is considered deprecated for all future releases, so it is just a matter of placing a standard query with additional operators.
GeoLocation.find({
"$nearSphere": {
"$geometry": {
"type": "Point",
"coordinates": [parseFloat(req.params.lng), parseFloat(req.params.lat)]
},
"$maxDistance": distanceInMeters
},
"loc.type": "Point"
},function(err,docs) {
// The documents are also mongoose document objects as well
});
See further options on $nearSphere
or other operators for options. The main difference here is $maxDistance
is in meters when a GeoJSON form is used, rather than radians where otherwise.
There is of course also the $geoNear
operator for the aggregation pipeline. This is available as of MongoDB 2.4 and can take other options such as a "query" to narrow results further. The other possible advantage here is that it will "project" a field into your results representing the "distance" from the query point. This can be used in other calculations or custom sorting:
GeoLocation.aggregate(
[
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": [parseFloat(req.params.lng), parseFloat(req.params.lat)]
},
"distanceField": "distance",
"maxDistance": distanceInMeters,
"spherical": true,
"query": { "loc.type": "Point" }
}},
{ "$sort": { "distance": -1 } } // Sort nearest first
],
function(err,docs) {
// These are not mongoose documents, but you can always cast them
}
);
Other differences to note are that in the standard query form the results are no longer limited to 100 documents as they are in the "command" form. The aggregation $geoNear
limits to 100 documents as results by default, but the number of documents returned can be tuned with an additional "limit" option to the pipeline command. The aggregate statement does not "sort" the results other than from the maximum documents to return from the search are the top results given the conditions, but they are not returned in order, so you would need to sort them as shown.
In either case you should be moving your code to use either of these forms as the command form is considered deprecated and will be removed in the future. Whether the mongoose API retains it's method as a "wrapper" for one of these forms is unknown, but mostly unlikely, so it is better to stick with the supported forms.
Upvotes: 5