Reputation: 4732
I wanted to do this:
model.User.aggregate([
//step 1 match criteria
{
$match: criteria
},
//step 2 skip
{
$skip: offset
},
//step 3 limit
{
$limit: limit
},
//step 4 sort by computed distance
{
$geoNear : {
near: {type: 'Point', coordinates: coords },
distanceField: 'currentCity.computed_distance',
includeLocs: 'currentCity.loc',
spherical: true,
uniqueDocs: true,
distanceMultiplier: 3963.2, //convert to miles (this number is the radius of the earth in miles)
}
}
],function(err,users){
if (err) return res.error(err);
if (!users.length) return res.error('no matched criteria');
res.apiResponse(users);
});
but the documentation of $geoNear states:
You can only use $geoNear as the first stage of a pipeline.
Reading the documentation, i see that i can simply move $match
inside of $geoNear
via the query
option. likewise, $limit
may be placed inside of $geoNear
via the limit
option. the one problem is, there is no equivalent for the $skip
option, so it looks as if facilitating pagination would not be possible? i'm really confused here, why $geoNear
can't be the 4th step in the pipeline. The goal of the query is simply to find the best n
matches, where n = limit
, then sort by nearest in proximity. is this even possible? I am having trouble finding an answer for this specific use case.
I suppose one solution might be to perform a query to select only ids matching documents, convert to list of ids, then do the aggregates with an $in
query like so:
model.User.find(criteria).skip(offset).limit(limit).select('_id').exec(function (err, userIds) {
var ids = [];
userIds.forEach(function(u){
ids.push(u._id);
});
model.User.aggregate([
{
$geoNear : {
query: { _id: {$in: $ids } },
near: {type: 'Point', coordinates: coords },
distanceField: 'currentCity.computed_distance',
includeLocs: 'currentCity.loc',
spherical: true,
uniqueDocs: true,
distanceMultiplier: 3963.2, //convert to miles (this number is the radius of the earth in miles)
}
}
],function(err,users){
if (err) return res.error(err);
if (!users.length) return res.error('no matched criteria');
res.apiResponse(users);
});
});
This would work, but ideally i could do it in 1 query if possible. any ideas greatly appreciated.
Upvotes: 1
Views: 2637
Reputation: 5884
One solution is this one:
result = db.cafes.aggregate([{
'$geoNear': {
'near': {
'type': 'Point',
'coordinates': [
-73.991084,
40.735863]},
'spherical': True,
'distanceField': 'dist',
'num': 20}
}, {
'$skip': 10
}])
There is also a better solution with this approach:
ids = [42]
result = db.command(
'geoNear', 'cafes',
near={
'type': 'Point',
'coordinates': [
-73.991084,
40.735863]},
spherical=True,
minDistance=268,
query={
'_id': {
'$nin': ids}},
num=10)
And a really nice explanation on speed and issues over here:
https://emptysqua.re/blog/paging-geo-mongodb/
Upvotes: 1