Search code examples
javascriptnode.jsnode-async

mongodb query inside node js eachOf loop


I want to add have nested DB query inside a eachOf loop which should be synchronous. Tried so many combinations and which but nothing works for inside of foreach loop.

   async.eachOf(nc.virtual_devices, function (vd) {
         ///////// This code work fine /////////////
        var domain = extractDomain(vd.api_url);
        vd.raw_api_ip = vd.api_ip;
        vd.api_ip = getProxiedPath(vd.api_ip);
        vd.raw_api_url = vd.api_url;
        vd.api_url = nconf.get('PROXIED_PATH_SCHEME') + vd.api_ip + vd.api_url.split(domain)[1];
          // Path to websocket notifications
        vd.ws_url = nconf.get('PROXIED_PATH_SCHEME') + vd.api_ip + vd.notification_base_uri;

     //// THIS CODE IS NOT FINE //////////////
        if (nc.type === 'XXX'){

            var promise = new Promise (function (resolve,reject) {

            console.log("********XX VD TYPE **********");
            console.log(JSON.stringify(vd));
            console.log("VD ID VALUE IS ", vd.id);
            var newID = (vd.id).replace(/\d_/, "");
            console.log("VD ID VALUE IS ", newID);

            var _idofSubgroup;
            var labeltoSearch = nc.type + ' ' + nc.version;
            pattern = "/^" + newID + "/i";
            test = _idofSubgroup;
            pattern = newID;
            console.log(pattern);

            db.collection('subgroups').findOne({label: labeltoSearch}, function (err, result) {
                console.log(result._id);
                _idofSubgroup = result._id;

                db.collection('exploreposts').find({subgroup: result.id_}, {title: {"$regex": pattern}}).toArray(function (err, results) {
                    console.log(results);
                })
        });
        })
    }
  });

Tried with promise inside it but that also in pain. This is my tried code which is not working fine. Any suggestion would be appreciated., as simply said i have been stuck in callback hell

   async.eachOf(nc.virtual_devices, function (vd) {
         ///////// This code work fine /////////////
        var domain = extractDomain(vd.api_url);
        vd.raw_api_ip = vd.api_ip;
        vd.api_ip = getProxiedPath(vd.api_ip);
        vd.raw_api_url = vd.api_url;
        vd.api_url = nconf.get('PROXIED_PATH_SCHEME') + vd.api_ip + vd.api_url.split(domain)[1];
          // Path to websocket notifications
        vd.ws_url = nconf.get('PROXIED_PATH_SCHEME') + vd.api_ip + vd.notification_base_uri;

     //// THIS CODE IS NOT FINE with promises also  //////////////
        if (nc.type === 'XXX'){

            var promise = new Promise (function (resolve,reject) {

            console.log("********XX VD TYPE **********");
            console.log(JSON.stringify(vd));
            console.log("VD ID VALUE IS ", vd.id);
            var newID = (vd.id).replace(/\d_/, "");
            console.log("VD ID VALUE IS ", newID);

            var _idofSubgroup;
            var labeltoSearch = nc.type + ' ' + nc.version;
            pattern = "/^" + newID + "/i";
            test = _idofSubgroup;
            pattern = newID;
            console.log(pattern);

            db.collection('subgroups').findOne({label: labeltoSearch}, function (err, result) {
                console.log(result._id);
                _idofSubgroup = result._id;
                resolve ({id_:_idofSubgroup,pattern1 : pattern});
            })
        });
        promise.then (function(result) {
            console.log(result.id_);
            console.log(result.pattern1);
                db.collection('exploreposts').find({subgroup: result.id_}, {title: {"$regex": result.pattern1}}).toArray(function (err, results) {
                    console.log(results);
                })
        },function (err){
            console.log (err);
       })


    }
  });

Solution

  • It doesn't seem you need to use async.eachOf, but async.each() or async.eachSeries().

    This is untested but it would look something like

    async.eachSeries(nc.virtual_devices, function iteratee(vd, cb) {
        console.log('calling iteratee()')
        
        var domain = extractDomain(vd.api_url);
        vd.raw_api_ip = vd.api_ip;
        vd.api_ip = getProxiedPath(vd.api_ip);
        vd.raw_api_url = vd.api_url;
        vd.api_url = nconf.get('PROXIED_PATH_SCHEME') + vd.api_ip + vd.api_url.split(domain)[1];
         // Path to websocket notifications
        vd.ws_url = nconf.get('PROXIED_PATH_SCHEME') + vd.api_ip + vd.notification_base_uri;
    
        // skip the rest if type is XXX;
        // you need to explicitedly call the original callback i.e. cb
        // note the use of return to prevent execution of the rest of the code
        if (nc.type !== 'XXX')
            return cb(null); // or cb();
    
        console.log("********XX VD TYPE **********");
        console.log(JSON.stringify(vd));
        console.log("VD ID VALUE IS ", vd.id);
        var newID = (vd.id).replace(/\d_/, "");
        console.log("VD ID VALUE IS ", newID);
    
        // I have no idea what is going here
        var _idofSubgroup;
        var labeltoSearch = nc.type + ' ' + nc.version;
        var pattern = "/^" + newID + "/i";
        test = _idofSubgroup;
        pattern = newID;
        console.log(pattern);
        
        // we're going to use waterfall here as you have 2 async operations, where one is dependent on the other 
        async.waterfall([
            function getSubgroup(cb1) {
                console.log('calling getSubgroup')
                db.collection('subgroups').findOne({ label: labeltoSearch }, function (err, subgroup) {
                    // if an error occurs, stop waterfall-loop
                    // you do this by passing the error in the callback
                    // again note the use of return here to prevent execution of the rest of the code
                    if (err) return cb1(err);
                    // pass the data to the next task
                    cb1(null, subgroup, pattern);
                });
            },
            function getPosts(subgroup, pattern, cb2) {
                // we will only get here if the last task ^ went through
                console.log('calling getPosts')
                db.collection('exploreposts').find({ subgroup: subgroup._id, title: { $regex: pattern }}).toArray(function (err, posts) {
                    // if an error occurs, stop waterfall-loop
                    if (err) return cb2(err);
                    // do something with posts
                    console.log('posts', posts);
                    // otherwise, keep going
                    // since there are no more waterfall-tasks, waterfall ends
                    cb2();
                });
            }
        ], function (err) {
            console.log('waterfall() done');
            // if an error occurred during the waterfall-loop, it will come down here
            // we will let the original callback i.e. cb deal with this error though
            if (err) return cb(err);
            // otherwise we're done
            // we will let the original callback know everything went well by calling it without any error
            cb();
        });
        
        // you could have also simply do
     // ], cb);
        
    
    }, function (err) {
        console.log('eachSeries() done');
        // handle any error that came
        console.log(err);
        // send response
    });
    

    I purposely name the variables and functions so that you get an idea.

    Follow the logs if there are any issues.