我有以下代码。使用$ geoNear,我找到最接近给定gps坐标的中转站点。正如你们可能知道的那样,$ geoNear只返回最近点的位置(loc)。要获取最近的停靠点的详细信息,我使用位置查询STOPS集合。
问题是,它随机返回undefined。当我从mongo shell查询停止集合时,使用agency_key和loc参数确认数据存在。我怀疑这个问题可能是由异步引起的,但我无法找出原因。 我试图简化我的问题 ' 在forEach循环中调用异步函数'但到目前为止我什么都没有。
为什么它返回undefined?
...
TmStop.aggregate([{
$geoNear: {
near: [lon, lat],
maxDistance: radiusInDegrees,
includeLocs: "distance.location",
distanceField: "distance.calculated",
query: {
agency_key: {
$in: agencyKeys
}
}
}
}, {
$project: {
route_id: 1,
route_type: 1,
direction_id: 1,
"distance.calculated": 1,
"distance.location": 1
}
}])
.exec(function(e, results) {
if (e) {
console.log('e->', e.errmsg);
var res = "Something went wrong with the database: " + e;
cb(e, res);
} else if (!e) {
if (results.length) {
console.log('results->', results.length);
var i = 0;
results.forEach(function(result, index) {
console.log(index, result);
Stop.find({
agency_key: {
$in: agencyKeys
},
loc: result.distance.location
})
.exec(function(e, stop) {
if (e) {
throw new Error('Error getting stop due to:' + e);
}
var obj = {};
obj.route_id = result.route_id;
obj.route_type = result.route_type;
obj.direction_id = result.direction_id;
obj.distance = result.distance.calculated;
obj.locationUsed = result.distance.location;
// console.log('### ', index, ' ####');
// console.log('@Stop.find agencyKeys-> ', agencyKeys);
// console.log('@Stop.find loc->', result.distance.location);
// console.log(stop[0]);
obj.stop = stop[0];
objArr.push(obj);
i++;
if (i === results.length) {
cb(e, objArr);
}
});
}); //end of forEach
} else {
cb(e, []);
}
}
});
答案 0 :(得分:1)
当然。你有一个.forEach()
循环调用异步函数而不等待任何(或更重要的是"所有"调用)完成。所以你的循环迭代需要尊重被调用的内部函数的完成调用。
一个很好的工具就是async库,出于安全考虑,我们将调用async.mapLimit
(因为你发出了一个数组),这将允许指定数量的操作同时运行,而不是直到堆栈限制(或者可能超出不受控制的.forEach()
或for
循环)。
因此,在聚合操作的回调中,将代码清单改为:
// If aggregation query returned anything
if (results.length) {
console.log('results->', results.length);
async.mapLimit(results,10,function(result,callback) {
Stop.findOne({
"agency_key": { "$in": agency_keys },
"loc": result.distance.location
},function(e,stop) {
result.stop = stop; // remember that result is already a plain object
callback(e,result);
});
},cb) // cb is automatically passed e and results
} else {
cb(e,results); // then the array was empty
}
有一些改进,最值得注意的是,当您处理从常规.find()
操作返回的mongoose文档时,您可能已经注意到您无法向它们添加新属性,因为它们是mongoosse文档(一个更复杂的对象)而不是普通对象。他们只是伪装成序列化和辅助访问器方法之一。
但.aggregate()
的结果实际上只是一个普通对象"。因此,不需要将对象属性复制到新对象只是为了分配新属性。为了将来的参考,您不需要为#34; Mongoose文档"与您在商家信息中一样明确,但只需在文档上调用.toObject()
即可。结果返回一个"普通对象"。但是在这里需要的代码中,它只是一个简单的任务。
还有.findOne()
而非.find()
的使用。在您的代码中注意到,您只是在寻找单数匹配并引用结果的第一个元素。因此只需要一个结果。查询本身也可能无论如何都会返回单个结果,因为来自" near"没有其他查询参数,但我真的没有掌握这些信息。
最后当然还有.map()
或更确切地说.mapLimit()
,您的所有意图是简单地从{{1}向results
数组的每个元素添加一个新属性。然后使用additional属性返回该元素。每次迭代都将使用修改后的数据发出提供的Stop
,然后在完成所有迭代时调用最终回调,该回调将包含返回的所有元素的发射数组。这基本上是callback
的内置参数,所以它只是以该形式提供,而不是包装在另一个函数中。