Closing mongodb connection and getting correct results with multiple concurrent asynchronous requests
I am new to Mongo and Node. I am currently using Mongoskin and Bluebird to communicate with db connection and queries (as suggested here: https://stackoverflow.com/a/1193475/ ... ).
I have three collections: users, bindings and maps.
The Binders collection contains information about Maps for each user. Each document in Binders has the following properties:
User Id <--- that refers to the User owning the Card
Card Code <--- that refers to a Card
Count <--- that refers to the number of cards owned by the User
I prefer to have a separate collection of maps so that when the Map changes, it changes for all user bindings.
Now I want to get an array for a given user, for example:
[{card: {card document}, count: 4}, ...]
I have the following problems:
- the db connection must be closed after all asynchronous callbacks have been called
- an array of cards should be returned after the last db.collection ('cards'). find returns results
I know my following code is wrong, but might be a starting point for discussion:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var promiseCards;
//console.log("------ binderCards: ", binderCards);
var cards = [];
for (var i = 0; i < binderCards.length; i++) {
var binderCard = binderCards[i];
promiseCards = db.collection('cards').find({Code: binderCard.Code}).toArrayAsync();
promiseCards.then(function(cardsDB){
if(cardsDB){
//console.log("Cards found: ",binderCard.Code, cardsDB);
for (var i = 0; i < cardsDB.length; i++) {
cardsDB[i].count = binderCard.count;
};
cards.concat(cardsDB);
}
});
}
promiseCards.then(function(){
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
I am struggling to figure out how to properly handle the aphorized successful call to send the whole array and close the db connection.
I think I should try to build a promise before the for loop and use it to bind the request to the promises cards and finally bind the db.close () and res.json (cards) statements.
[EDIT] Perhaps the simplest solution is to simply use the $ in filter inside a single db.collection ('cards'). find ({Code: {$ in: [bindersCodeArray]}}). toArrayAsync (); and avoid this for the loop:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var binderCodes = binderCards.map(function(element){
return element.Code;
});
var promiseCards = db.collection('cards').find({Code: {$in: binderCodes} }).toArrayAsync();
promiseCards.then(function(cards){
var bindersDictionary = {};
for (var i = 0; i < binderCards.length; i++) {
bindersDictionary[binderCards[i].Code] = binderCards[i].count;
};
for (var i = 0; i < cards.length; i++) {
cards[i].count = bindersDictionary[cards[i].Code];
};
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
However, I am curious if there is an elegant way to solve this puzzle using promises.
source to share
I would expect that using $ in and array might have limits on the number of binders you can pass and affect query performance. You can also try doing this with async # map . eg:.
...
function(binders) {
async.map(binders, cardsForBinders, function(err, bindersWithCards) {
// TODO: close connection here.
}
function cardsForBinders(binder, callback) {
// 1. find cards for binder.
// 2. prepare transformed response: binderWithCards.
callback(null, binderWithCards);
}
}
...
source to share