2017-10-07 5 views
1

J'ai une liste de produits, chacun avec ses propres options. Par exemple:Remplir un objet référencé par un autre objet

  • Robe bleue (S - L)
  • Robe rouge (XS - S - M)

Robe bleue et robe rouge sont les produits, S , L, XS, S et M sont les options. Le modèle d'options a une référence au modèle de produit, je voudrais récupérer tous les produits et puis énumérer leurs propres options.

Je voudrais y arriver avec une seule requête, mon problème est que je pars du produit qui n'a aucun lien avec ses options. Donc, je commence à trouver tous les produits, et avec un imbriqué puis avec une boucle foreach je reçois toutes ses options. Ensuite, j'essaie d'assigner l'option à l'objet produit (dans mon cas productElem à l'intérieur d'un forEach), mais lorsque je le récupère hors de la portée, il est vide. Comment remplir les options à partir d'une requête des produits?

Schéma du produit:

var schema = new Schema({ 
    imagePath: {type: String}, 
    title: {type: String, required: true}, 
    description: {type: String, required: true} 
}); 

Option schéma:

var productOptionSchema = new Schema({ 
    type: {type: String, enum: ['grams'], default: 'grams', required: true}, 
    value: {type: String, required: true}, 
    price: {type: Number, required:true}, 
    product: {type: Schema.Types.ObjectId, ref: 'User', required:true} 
}); 

Ici, j'essayer d'obtenir les options après avoir trouvé les produits

router.get('/shop/products/list', isLoggedIn, function (req, res, next) { 
    Product.find() 
    .then(function (products) { 
     products.forEach(function(productElem) { 
     ProductOption.find({product: productElem._id}) 
      .then(function (options) { 
      productElem['options'] = []; 
      options.forEach(function(optionElem) { 
       productElem['options'].push(optionElem); 
      }); 
      }); 
     }); 
     res.render('shop/listProduct', {user:req.user, csrfToken: req.csrfToken(), messages:messages, partialCustom: 
     }); 
    }) 
    .catch(function (err) { 
     console.log('Error ' + err.code + ': ', err.message); 
     res.status(500).send('Failed to get the Product List from the DB: ' + err); 
    }); 
}); 
+0

votre problème n'est pas très clair. Veuillez modifier la question pour la rendre plus compréhensible. –

Répondre

0

Il y a quelques failles dans votre code. la boucle forEach essaie de trouver toutes les options basées sur product ID, ce qui ressemble à une approche évidente ici, mais le problème ici est la nature asynchrone de la méthode find().

En raison de la nature asynchrone de la boucle find(), forEach se rempli sans attendre le résultat de l'individu find(), à la suite dont options n'est pas encore peuplée. Et après la boucle, il rend juste 'shops/listProduct', qui n'a évidemment pas les options du produit.

Ce que vous pouvez faire est de pousser tous les find() dans un tableau promises, attendez que toutes les promesses de retour en utilisant Promise.all(). Ne res.render('shops/listProduct',{...}) après toutes les promesses sont réussies.

Autre méthode:

J'ai une méthode beaucoup plus simple pour obtenir ce que vous voulez à l'aide aggregation.

Essayez ceci:

ProductOption.aggregate([{ 
    $group : { 
     _id : product, 
     options : {$push : "$$ROOT"} 
    } 
},{ 
    $lookup : { 
     from : "products", 
     localField : "_id", 
     foreignField : "_id", 
     as : "product" 
    } 
},{ 
    $unwind : { 
     path : "$product", 
     preserveNullAndEmptyArrays : true 
    } 
},{ 
    $project : { 
     _id : "$product._id" 
     imagePath : "$product.imagePath", 
     title : "$product.title", 
     description : "$product.description", 
     options : "$options" 
    } 
}],function(err,result){ 
    //result will have all the products with their options 
}); 

$group regrouperons les options en fonction du product (i.e product ID) $lookup remplira l'objet produit, et $project vous renverra le résultat comme vous le souhaitez.

Lisez à propos de mongodb Aggregation, $group, $lookup, $project pour mieux le comprendre.