2013-05-06 79 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