0

There are two collections products and stock in my database.Each product have multipple stock with different supplier and attributes of products. I have selected the products from products collection and run a loop for each product. I need to append price and offer price from the stock collection to products i have selected already. I think the for loop compleated its execution before executiong the find method on stock collection.I need to execute everything in the loop in serial manner(not asynchronous). Please check the code below and help me to solve this. I'm new in node.js and mongodb

collection  = db.collection('products');
collection.find().toArray(function(err, abc) {
var finalout = [];
        for( var listProducts in abc){
               var subfinal = {
                 '_id'          :abc[listProducts]['_id'],
                 'min_price'    :abc[listProducts]['value']['price'],
                 'stock'        :abc[listProducts]['value']['stock'],
                 'name'         :abc[listProducts]['value']['name'],
                 'price'        :'',
                 'offer_price'  :'',
               };
               collection  = db.collection('stock');
               collection.find({"product":abc[listProducts]['_id'] ,"supplier":abc[listProducts]['value']['def_supplier']}).toArray(function(err, newprod) {
                  for( var row in newprod){
                       subfinal['price']      =   newprod[row]['price'];
                       subfinal.offer_price   =   newprod[row]['offer_price']; 
                   }
                   finalout.push(subfinal);
             });
        }
        console.log(finalout);
  });
Premjith
  • 163
  • 2
  • 13
  • You can't and you should not want to: http://stackoverflow.com/questions/12030248/what-is-the-right-way-to-make-a-synchronous-mongodb-query-in-node-js – Simon H Jan 27 '16 at 06:52

1 Answers1

0

Yes, the loop is running and starting the get requests on the database all at the same time. It is possible, like you said, to run them all sequentially, however it's probably not what you're looking for. Doing it this way will take more time, since each request to Mongo would need to wait for the previous one to finish.

Considering that each iteration of the loop doesn't depend on the previous ones, all you're really looking for is a way to know once ALL of the operations are finished. Keep in mind that these can end in any order, not necessarily the order in which they were initiated in the loop.

There's was also an issue in the creation of the subfinal variable. Since the same variable name is being used for all iterations, when they all come back they'll be using the final assignment of the variable (all results will be written on the same subfinal, which will have been pushed multiple times into the result). To fix that, I've wrapped the entire item processing iteration into another function to give each subfinal variable it's own scope.

There are plenty of modules to ease this kind of management, however here is one way to run some code only after all the Mongo calls are finished using a counter and callback that doesn't require any additional installs:

var finished = 0; // incremented every time an iteration is done
var total = Object.keys(abc).length; // number of keys in the hash table

collection  = db.collection('products');
collection.find().toArray(function(err, abc) {
    var finalout = [];

    for( var listProducts in abc){
        processItem(listProducts);
    }

});


function processItem(listProducts) {
    var subfinal = {
        '_id'          :abc[listProducts]['_id'],
        'min_price'    :abc[listProducts]['value']['price'],
        'stock'        :abc[listProducts]['value']['stock'],
        'name'         :abc[listProducts]['value']['name'],
        'price'        :'',
    'offer_price'  :'',
    };
    collection  = db.collection('stock');
    collection.find({"product":abc[listProducts]['_id'] ,"supplier":abc[listProducts]['value']['def_supplier']}).toArray(function(err, newprod) {
        for( var row in newprod){
            subfinal['price']      =   newprod[row]['price'];
            subfinal.offer_price   =   newprod[row]['offer_price']; 
        }
        finalout.push(subfinal);
        finished++;
        if (finished === total) { // if this is the last one to finish.
            allFinished(finalout);
        }
    });
}


function allFinished(finalout) {
    console.log(finalout);
}

It could be written more concisely with less variables, but it should be easier to understand this way.

pohlman
  • 331
  • 1
  • 6
  • 1
    Thank you Pohlman,Thank you for your quick response. But the code seems to be not working. I got the finalout with all has same values. that means the last value from the abc. – Premjith Jan 27 '16 at 08:01
  • Sorry, this should have caught my eye since I've dealt with it so many times! I've updated my answer to fix this variable scoping problem. – pohlman Jan 27 '16 at 16:47