A
A
Alexander Drozdov2015-04-18 20:02:09
MongoDB
Alexander Drozdov, 2015-04-18 20:02:09

How to bind two models into one via iteration in node.js and mongoos?

There are two simple, one-to-many models.
1 - product
2 - product_info (there is a product_id field)
So, I'm trying to john all this. But it doesn’t work for me, it seems to me that something is certainly not right, but the problem is that the forEach loop runs faster than the data is pushed into the final array. And finally def.resolve(result); empty

var mongoose = require('mongoose');
var Q = require('q');

var catalog = function(app) {
    var product = mongoose.model('product', require('../model/product'));
    var productInfo = mongoose.model('product_info', require('../model/ProductInfo'));

    var getAllProducts = function() {
        var result = [];
        var def = Q.defer();

        product.find(function(err, products) {
            products.forEach(function(product) {
                productInfo.find({product_id: product._id}, function(err, info) {
                    product.info = info;
                    result.push(product);
                });
            });

            def.resolve(result);
        });

        return def.promise;
    };

    return {
        getAllProducts : getAllProducts
    };
};

module.exports = catalog;

and here we call
var products = require('../src/catalog')(app).getAllProducts();

        products.then(function (products) {
            console.log(products);
            res.render('index', { products : products });
        });

How to do all this more intelligently? thank.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
I
Ilya Shatokhin, 2015-04-18
@bagzon

You have asynchronous code. Logically, def.resolve(result) will run before any of productInfo.find ends .
You need control over the execution of asynchronous code, like async .

product.find(function(err, products) {
  async.each(products, function (product, cb) {
    productInfo.find({product_id: product._id}, function(err, info) {
      product.info = info;
      cb(err);
    });
  }, function (err) {
    def.resolve(products);
  });
});

Another version of your code in one request and without async:
product.find(function(err, products) {
 var p_ids = products.map(function (product) { // создаем массив id продуктов
    return product._id; 
  });

  productInfo.find({product_id : {$in:  p_ids}}, function(err, infos) { // запрашиваем info для всех совпадающих id продуктов в массиве
    var obj = infos.reduce(function(a, b) { // создаем коллекцию для быстрого поиска, где ключ - id продукта
      return a[b.product_id ] = b;
    }, {});

    products.forEach(function (product) {
      product.info = obj[product._id];
    });

    def.resolve(products);
  }));
});

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question