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

+0

동물은 없으며 청과물 만 있습니다. 이 경우에 .. 토마토는 과일과 채소로 간주됩니다. – Rolando

+0

개는 과일입니까, 야채입니까? :-) – numbers1311407

답변

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은이 btw와 같은 콜백 관리에 우수합니다.

업데이트

과 같이 다시 작성하기 위해 한 번만 각 열매를 얻으려면, 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

+0

Doh, 귀하의 의견을 보자 마자 async.parallel이 함수를 배열로 사용한다는 사실을 잊어 버렸습니다. 코드를 업데이트했습니다. 다시 시도해보고 더 잘 작동하는지 확인하십시오. –

+0

{NonEdible.findOne ({ "name": plant}, done}} 세 번째 함수를 추가하려고하면 어떤 이유로 과일과 야채의 첫 번째와 두 번째 함수를 처리 할 수 ​​있습니다. 하지만 제 3의 기능 (NonEdible)은 ... 왜 그런가요? 이것이 어떻게이 세 번째 기능의 추가를 지원할 수 있습니까? – Rolando

0

find은 비동기 함수이며 mongo 데이터베이스에 요청합니다. 이는 두 가지를 의미합니다.

  1. 함수는 결과를 즉시 반환하지 않습니다. 몽구스의 find 기능은 매우 일반적인 비동기 패턴을 따릅니다. 그것은 결과 또는 오류와 함께 호출 할 "콜백"기능을 허용합니다. 노드 규칙에 따라 첫 번째 인수가 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. 
    
  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 같은 라이브러리를 사용하거나 뭔가를 쓸 수있다 : 당신이 처리 결과를 할 방법이 명확하지 단순히 결과 CONCAT 여기에 두 예는, 보내

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); 
에 대해 하나의 문서를 반환 할 수 있습니다 한 방법입니다

두 가지 쿼리의 결과를 정렬하고 페이지 매김하거나 다른 방식으로 제한해야하는 경우 이러한 종류의 작업이 훨씬 더 복잡해지고 필요한 경우 동일한 컬렉션에 문서를 보관하는 것이 좋습니다.

+0

위의 예를 기반으로하여 ...와 같이 주문을 보장 할 수있는 방법이 있다면 이상적입니다. res.send에 의해 반환 된 결과는 [ "Tomato Document (fruit or veggies collection에서 두 가지 모두 동일하므로 하나 선택)", "Carrot Document (채소에서 가져온 것)", "Orange Document (과일 컬렉션에서) "]. 귀하가 제공하는 답변은이 주문을 보장하지 않는 것으로 보입니다. – Rolando

+0

주문을 보장합니다. 두 경우 모두 배열은 과일, 야채. 두 가지 레코드가 모두 반환 되었으면 좋겠는지 또는 중복이있는 경우 우선 순위를 가져야하는지 (하나는 과일 문서, 하나는 야채 문서, 다른 하나는 식물성 문서 임)의 질문에서 분명하지 않으므로 고유하지 않습니다. , 그러나 그들은 같은 문서가 아닙니다). – numbers1311407

+0

@Damascusi 왜 내 대답을 보지 않으시겠습니까? 테스트리스트 배열과 동일한 순서를 유지합니다. –

관련 문제