Koningh
Koningh

Reputation: 628

Creating and updating documents synchronously with mongoose

I'm wondering what the best approach would be to realize the following situation in Node.js and mongoose:

I'm having a collection with users and a collection with groups. I want users to be able to create new groups and add people to them (very similar to people creating a new whatsapp group). I use the following schemas for the user and group documents:

var userSchema = mongoose.Schema({ 
    email: String, 
    hashed_password: String,
    salt: String,
    name: String,
    groups: [{
        _id: { type: Schema.Types.ObjectId, required: true, ref: 'groups' }, 
        name: String
    }]
});

var groupSchema= mongoose.Schema({ 
    name: String,
    creator: Schema.Types.ObjectId,
    users: [{
        _id: { type: Schema.Types.ObjectId, required: true, ref: 'users' }, 
        rankInGroup: { type: Number, required: true }
    }]
});

At this moment I have a function that takes the following arguments: the email address (groupCreator) of the user who is creating the group, the name of the new group (newGroupName), and the userids of the users (newGroupMembers) that need to be added to the group. This function first finds the users that need to be added to the group (using this method) and then adds the user ids of these users to the users array in the group document like this:

function(groupCreator, newGroupName, newGroupMembers , callback) {
    userModel.findOne({
        email: emailGroupCreator
    }, function(err,creator){
        //load document of user who creates the group into creator variable
        var newGroup = new groupModel({ 
            name: newGroupName, 
            creator: creator._id, 
            users: [{_id: creator._id, rank: 0}] //add group creator to the group
            });

        userModel.find({
            email: { $in: newGroupMembers }
        }, function(err,users){                         
            //for now assume no error occurred fetching users
            users.forEach(function(user) {                              
                newGroup.users.push({_id: user._id, rank: 0}); //add user to group
                user.groups.push({_id:newGroup._id,name:newGroup.name}); //add group to user
            }
            newGroup.save(function (err) {
            //for now assume no error occurred
            creator.groups.push({_id: newGroup._id, name: newGroup.name}); //add group to creator
            creator.save(function (err) {
                //for now assume no error occurred

                /* save/update the user documents here by doing something like
                newMembers.forEach(function(newMember) {
                    newMember.save(function (err) {
                        if(err){
                            callback(500, {success: false, response: "Group created but failure while adding group to user: "+newMember.email});
                        }
                    });
                });
                callback(200, {success: true, response: "Group created successfully!"});
                */ 
            });
        }
    });
}

So I want this function to:

The problem here is that the updating of all the user documents of the users added to the group happens asynchronously, but I want to be sure that all the user documents are updated correctly before I return a success or failure callback. How can I update all the user documents before I continue with the rest of the code (maybe not using a foreach)? Is my initial approach of retrieving all the user documents good or are there better ways to do this?

So the bottom line question is; how can I save multiple user documents and continue with the rest of the code (send a callback to notify success or failure) after all the save actions are performed, or is there a way to save all the documents at once?

NB The reason why I want (some) of the same information in both the user and the group document is because I don't want to load all the group info for a user if he logs in, only the basic group information. See also this source under the section many-to-many relationships.

Upvotes: 0

Views: 1286

Answers (1)

Koningh
Koningh

Reputation: 628

JohnnyHK pointed me in the right direction; async.each make it possible to iterate over the documents and update them one at a time. After that the rest of the code gets executed. This is how my code looks now:

async.each(groupMembersDocs, function (newMember, loopCallback) {
    //console.log('Processing user ' + newMember.email + '\n');
    userModel.update({
        email: newMember.email
    }, {
        $push: { 'groups' : {_id: newGroup._id, name: newGroup.name} }
    }, function (err, data) { 
        if(err){
            console.log("error: "+err);
            loopCallback('Failure.. :'+err);
        } else{
            newGroup.users.push({_id: newMember._id, rank: -1});
            loopCallback();
        }
    });
}, function (err){
    if(err){
        console.log("error: "+err);
        callback(500, {success: false, response: "Error while adding users to group"});
    } else{
        newGroup.save(function (err) {
            callback(201, {success: true, response: "Group created successfully"});
        });
    }
})

Upvotes: 1

Related Questions