2013-05-06 63 views
1

我使用nodejs + express + mongoose。假设我有两个模式和模型:“水果”和“蔬菜”。如何同步请求?

假设我有以下几点:

var testlist = ["Tomato", "Carrot", "Orange"]; 
var convertedList = []; 
// Assume res is the "response" object in express 

我婉能够在阵列中检查每个项目对“水果”和“蔬菜”集合分别将它们插入到一个转换的列表,其中番茄,胡萝卜和西兰花被他们各自的文件替换。

下面我有一些我认为会是的伪代码,但不知道该怎么做。

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

有动物吗?似乎在你的问题上有些混乱。 – numbers1311407 2013-05-06 14:56:30

+0

没有动物,只有水果和蔬菜。注意在这种情况下..番茄被认为是水果和蔬菜。 – Rolando 2013-05-06 14:57:32

+0

狗是水果还是蔬菜? :-) – numbers1311407 2013-05-06 14:58:36

回答

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); 
} 
+0

出于某种奇怪的原因,它出现Fruit.findOne({“name”:plant},done);永远不会被召唤。 – Rolando 2013-05-07 16:15:48

+0

Doh,只要我看到你的评论,我意识到我忘记了async.parallel将函数作为一个数组。我更新了代码,请再试一次,看看它是否更好。 – 2013-05-07 19:20:56

+0

当我尝试添加第三个函数(done){NonEdible.findOne({“name”:plant},done)}时,我注意到由于某种原因它能够处理第一个和第二个函数(Fruit and Vegetables)但不是第三个功能(NonEdible)...为什么是这样?这怎么能支持第三个函数的添加? – Rolando 2013-05-07 21:41:33

0

find是一个异步函数,它使所述蒙戈数据库的请求。这意味着两件事:

  1. 函数不会立即返回结果。猫鼬中的find函数遵循非常常见的异步模式。它接受一个“回调”函数,它将调用结果或错误。通过节点约定,如果第一个参数不为空,那就是错误。

    // 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. 
    
  2. 由于每个查询发射另一个请求关闭数据库,您通常希望如果可以的数目进行限制。在这种情况下,您可以通过使用mongo的$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(); 
}); 

注意的是,这里两个例子,只是CONCAT结果,并给他们,因为你想怎么处理结果尚不清楚。这意味着,例如,如果一份西红柿都在这两份名单中,它会在结果中出现两次,包括蔬菜和水果文件。

您还需要处理从猫鼬回来的任何错误。

编辑:唯一命名的文档

在您的评论的光,这是一种方式,你可能只返回一个文档番茄(或其他记录是既水果和蔬菜)

// 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); 

请注意,如果您需要对两个查询的结果进行排序和分页或以其他方式限制这两个查询的结果,则此类事情会变得复杂得多,并且如果您需要这样做,则可以考虑将文档保留在同一个集合中。

+0

理想情况下,如果有某种方法可以保证订单......就像......根据上面的例子,我希望res.send返回的结果是[“番茄文档(来自水果或蔬菜集合,假设两者是相同的,所以选择一个)”,“胡萝卜文档(来自蔬菜)”,“橙色文档(来自水果收藏) “。您提供的答案似乎不能保证这个顺序。 – Rolando 2013-05-06 20:15:29

+0

它确保订单。在这两种情况下,阵列将是水果,然后是蔬菜。然而,这并不是唯一的,因为从你是否想要返回记录或者如果有重复记录应该优先选择哪一个(一个是Fruit doc,一个是Vegetable doc;它们可能是相同的,但它们不是相同的文件)。 – numbers1311407 2013-05-06 20:26:12

+0

@Damascusi你为什么不看看我的答案?它保留与testlist数组相同的顺序。 – 2013-05-07 07:51:59