mr_anderson
mr_anderson

Reputation: 3

Mongoose save() returns before findOne()

I am implementing an approval system using Mongoose, Express and NodeJS.

Each post has 2 set of approvals.

I have implemented model as below:

Post Schema:
var PostSchema = new Schema({
UserId: { type: ObjectId, unique: true, default: null },
/..
.. Object related fields
../
    Approval1: Object, //chidschema objects of approver
    Approval2: Object
});

Approval Mapping Schema:
var ApprovalMapSchema = new Schema({
  UserId: { type: ObjectId, unique: true, default: null },
  Approver1Id: { type: ObjectId, default: null },
  Approver2Id: { type: ObjectId, default: null }
});

I am trying to find the mapping first in my function and then update the main post object on save.

I need to retrieve both approver IDs before saving new Post.

Please suggest a good way to go about it. I am currently doing:

  1. Save new Post object with Save //returns after 2nd step, i knw save in Mongoose in async but i do not want step 2 be called before this
  2. Get approver IDs with findOne on ApprovalMapSchema
  3. Update Post object with approver entries.

    post.save(function(err, doc) { if (err) {} else { console.log({success: true, msg: 'Successful created new post', content: doc}); } });

    /*var actId = activation._id, distributorId, ttlId;
    
    var approvalMap = ApprovalMap.findOne({ userId: myUserId }, function(err, appMap) {
        if (err) throw err;
        else{           
            Approver1Id = appMap.Approver1Id;
            Approver2Id = appMap.Approver2Id;
        }
    });
    
    // Create child objects after this and update as subdocument
    

This is not working for me. Please Help!

Upvotes: 0

Views: 531

Answers (2)

user2263572
user2263572

Reputation: 5606

I am having a difficult time understanding your question. But it sounds like you want to do something like this (you will need to modify this based on what you want to do, but hopefully it shows you one way of handling the async behavior):

    //This will run a query that returns a document where userId == myUserId

    ApprovalMap.findOne({ userId: myUserId }, function(err, appMap) {

        if (err) throw err;

        //Once the query is complete function(err, appMap) will be called
        //err will contain an error object if there was an error
        //appMap will be the data returned by the query.
        //within this block you can run another query
        //for example

        ApprovalMap.findOne({approved:'some variable'},function(err, secondQueryData){

            if (err) throw err;

            //after this query is complete function(err, secondQueryData) will be called
            //once again the err argument will be an error, and the 

            //secondQueryData argument will contain your return data
    
            //here you can do something with the data
            //for example, log the results

            console.log(secondQueryData)
        })
    });

As you can see, you can nest additional queries or steps within the callbacks of other queries. This will ensure things are run in the correct order. you may also want to checkout the npm async library, or a promise library like q or bluebird. They also have some great solutions for handling the async behavior in node.

Upvotes: 1

mr_anderson
mr_anderson

Reputation: 3

as per @user2263572 suggestion, went for async library.

this is how I implemented it now:

async.waterfall(
    [
        function(callback){
            ApprovalMap.findOne({ userId: myUserId }, function(err, appMap) {
                if (err) throw err;
                else{           
                    //..Create child approval elements and assign Ids from map
                    callback(err, Approver1Child, Approver2Child);
                }
            });
        },
        function(Approver1Child, Approver2Child, callback){
            var post = new Post(req.body);
            post.distApproval = Approver1Child;
            post.ttlApproval = Approver2Child;
            post.userId = myUserId;
            callback(null, post);
        }
    ], 
    function(err, post){
        //creating new order
        post.save(function(err, doc) {
            if (err) {
            }
            else
            {
                console.log({success: true, msg: 'Successful created new post', content: doc});
            }
        });
    }
);

Thanks a lot @user2263572!! Cheers!

Upvotes: 0

Related Questions