我使用的是nodejs + express + mongoose。假设我有两个模式和模型:“水果”和“蔬菜”。
假设我有以下内容:
var testlist = ["Tomato", "Carrot", "Orange"];
var convertedList = [];
// Assume res is the "response" object in express
我希望能够分别针对“fruits”和“vegetables”集合检查数组中的每个项目,并将它们插入转换后的列表中,其中Tomato,Carrot和Broccoli将替换为各自的文档。
下面我有一些我认为会有的伪代码,但不知道怎么做。
for(var i = 0; i < testlist.length; i++) {
var fruitfind = Fruit.find({"name":testlist[i]});
var vegfind = Vegetables.find({"name":testlist[i]});
// If fruit only
if(fruitfind) {
convertedList.push(fruitfindresults);
}
// If vegetable only
else if(vegfind) {
convertedList.push(vegfindresults);
}
// If identified as a fruit and a vegetable (assume tomato is a doc listed under both fruit and vegetable collections)
else if (fruitfind && vegfind) {
convertedList.push(vegfindresults);
}
}
// Converted List should now contain the appropriate docs found.
res.send(convertedList) // Always appears to return empty array... how to deal with waiting for all the callbacks to finish for the fruitfind and vegfinds?
最好的方法是什么?或者这甚至可能吗?
答案 0 :(得分:1)
假设每种水果/蔬菜中只有一种,并且你打算推两次收集的蔬菜两次。
var async = require("async"),
testlist = ["Tomato", "Carrot", "Orange"];
async.map(testlist, function (plant, next) {
async.parallel([function (done) {
Fruit.findOne({"name": plant}, done);
},
function (done) {
Vegetables.findOne({"name": plant}, done);
}], function (err, plants) { // Edited: before it was (err, fruit, veggie) which is wrong
next(err, plants);
});
},
function (err, result) {
var convertedList = [].concat(result);
res.send(convertedList);
});
注意:实际上没有测试过代码,但它应该可以工作。 The async module非常适合管理像这样的回调。
<强>更新强>
要只获取每个水果一次,async.parallel回调只需要像这样重写:
function (err, plants) {
next(err, plants[0] || plants[1]);
}
在.map回调中不再需要concat:
function (err, result) {
res.send(result);
}
答案 1 :(得分:0)
find
是一个异步函数,它向mongo数据库发出请求。这意味着两件事:
这些函数不会立即返回结果。 mongoose中的find
函数遵循非常常见的异步模式。它接受&#34;回调&#34;它将使用结果或错误调用的函数。按节点约定,如果第一个参数不为null,则表示错误。
// So typically you'd call find like this
SomeModel.find({your: 'conditions'}, function (err, results) {
if (err) {
// some error has occurred which you must handle
} else {
res.send(results);
}
})
// note that if code existed on the lines following the find, it would
// be executed *before* the find completed.
由于每个查询都会向数据库发送另一个请求,因此您通常希望限制该数量。在这种情况下,您可以使用mongo&#39; s $in
一次查找所有名称,而不是按名称查找每个水果/蔬菜。
考虑到这两点,您的代码可能如下所示:
// here *first* we're finding fruits
Fruit.find({name: {$in: testlist}}, function (err, fruits) {
// when the fruit request calls back with results, we find vegetables
Vegetable.find({name: {$in: testlist}}, function (err, vegetables) {
// finally concat and send the results
res.send(fruits.concat(vegetables));
});
});
要使两个请求并行发生,需要做更多的工作。您可以使用像async这样的库,或者自己写一些东西:
var fruits
, vegetables
, done = function () {
if (fruits && vegetables) {
res.send(fruits.concat(vegetables));
}
}
Fruit.find({name: {$in: testlist}}, function (err, docs) {
fruits = docs;
done();
});
Vegetable.find({name: {$in: testlist}}, function (err, docs) {
vegetables = docs;
done();
});
请注意,这里的两个示例都只是简单地结果并发送它们,因为它不清楚您希望如何处理结果。这意味着,如果番茄,例如,在两个列表中,它将在结果中出现两次,包括蔬菜和水果文件。
您还需要处理从猫鼬回来的任何错误。
修改:名称独特的文档
根据您的评论,这是您可能只返回一个番茄文档(或其他水果和蔬菜记录)的一种方式
// after retrieving fruits and vegetables, create a map which will
// serve to weed out docs with duplicate names
var map = {};
fruits.forEach(function (fruit) {
map[fruit.name] = fruit;
});
vegetables.forEach(function (vegetable) {
map[vegetable.name] = vegetable;
});
var results = [];
// this would sort by name
Object.keys(map).sort().forEach(function (key, i) {
results[i] = map[key];
});
res.send(results);
请注意,如果您需要排序和分页或以其他方式限制两个查询的结果,这种事情会变得更加复杂,如果您需要,您可能宁愿考虑将文档保留在同一个集合中。