我想这样做:
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);
});
但 $ geoNear 的文档声明:
您只能使用$ geoNear作为管道的第一阶段。
阅读文档,我看到我只需通过$match
选项将$geoNear
移到query
内。同样,$limit
可以通过$geoNear
选项放在limit
内。一个问题是,$skip
选项没有等价物,所以看起来好像不便于分页?我真的很困惑,为什么$geoNear
不能成为管道中的第四步。查询的目标只是找到最佳的n
匹配项,其中n = limit
,然后按近似值排序。这有可能吗?我无法找到这个特定用例的答案。
我认为一个解决方案可能是执行查询以仅选择匹配文档的ID,转换为ID列表,然后使用$in
查询进行聚合,如下所示:
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);
});
});
这可行,但理想情况下,如果可能,我可以在1个查询中执行此操作。任何想法都非常感激。
答案 0 :(得分:1)
这是一个解决方案:
result = db.cafes.aggregate([{
'$geoNear': {
'near': {
'type': 'Point',
'coordinates': [
-73.991084,
40.735863]},
'spherical': True,
'distanceField': 'dist',
'num': 20}
}, {
'$skip': 10
}])
这种方法还有更好的解决方案:
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)
关于速度和问题的一个非常好的解释: