Sous-documents MongoDB mongoose créés deux fois

J’utilise un formulaire simple qui peut être utilisé pour enregistrer un article sur un site Web.

le back-end ressemble à ceci:

// Post new article app.post("/articles", function(req, res){ var newArticle = {}; newArticle.title = req.body.title; newArticle.description = req.body.description; var date = req.body.date; var split = date.split("/"); newArticle.date = split[1]+'/'+split[0]+'/'+split[2]; newArticle.link = req.body.link; newArticle.body = req.body.body; var platforms = req.body.platforms; console.log(platforms); Article.create(newArticle, function(err, createdArticle){ if(err){ console.log(err.message); } else { var counter=0; platforms.forEach(function(platform){ var platformed=mongoose.mongo.ObjectID(platform); Platform.findById(platformed, function(err, foundPlatform){ if(err){ console.log(err); } else { counter++; foundPlatform.articles.push(createdArticle); foundPlatform.save(); createdArticle.platforms.push(foundPlatform); createdArticle.save(); if(counter==platforms.length){ res.redirect('articles/' + createdArticle._id); } } }); }); } }); }); 

Le champ plates-formes est transmis au back-end sous la forme d’un tableau de chaînes, une chaîne étant un objectID. Lorsque les plates-formes ne contiennent qu’une chaîne, c’est-à-dire une plate-forme à lier, tout fonctionne correctement. Lorsque les plates-formes contiennent plusieurs chaînes. l’article créé contient des copies de chaque plate-forme. Ou parfois seulement des doublons de certaines plates-formes

Des idées?

UPDATE 1: Schéma de l’article: var mongoose = require (“mongoose”);

 var articleSchema = new mongoose.Schema({ title : Ssortingng, description : Ssortingng, link : Ssortingng, date : Ssortingng, body : Ssortingng, platforms : [ { type: mongoose.Schema.Types.ObjectId, ref: "Platform" } ] }) module.exports = mongoose.model("Article", articleSchema); 

Schéma de la plateforme:

 var mongoose = require("mongoose"); var platformSchema = new mongoose.Schema({ name : Ssortingng, category : Ssortingng, contacts : [ { type: mongoose.Schema.Types.ObjectId, ref: "Contact" } ], website : Ssortingng, country : Ssortingng, contactInformation : Ssortingng, businessModelNotes : Ssortingng, source : Ssortingng, generalNotes : Ssortingng, projects : [ { type: mongoose.Schema.Types.ObjectId, ref: "Project" } ], articles : [ { type: mongoose.Schema.Types.ObjectId, ref: "Article" } ], privacy : Ssortingng, comments : [ { type: mongoose.Schema.Types.ObjectId, ref: "Comment" } ] }); module.exports = mongoose.model("Platform", platformSchema); 

La boucle forEach de votre tentative ne reconnaît pas l’achèvement du rappel de la méthode asynchrone findById() avant la prochaine itération. Vous devez utiliser l’une des méthodes de la bibliothèque async async.each , async.whilst ou async.until qui sont équivalentes à une boucle for et attendront que le rappel async soit async.until avant de passer à la prochaine itération (en d’autres termes, une pour la boucle qui va céder).

Par exemple:

 var platform_docs = []; async.each(platforms, function(id, callback) { Platform.findById(id, function(err, platform) { if (platform) platform_docs.push(platform); callback(err); }); }, function(err) { // code to run on completion or err console.log(platform_docs); }); 

Pour toute l’opération, vous pouvez utiliser la méthode async.waterfall() qui permet à chaque fonction de transmettre ses résultats à la fonction suivante.

La première fonction de la méthode crée le nouvel article.

La deuxième fonction utilise la fonction d’utilitaire async.each() pour parcourir la liste des plates-formes, effectuer une tâche asynchrone pour chaque ID afin de mettre à jour la plate-forme à l’aide de findByIdAndUpdate() , puis renvoyer les résultats de la requête de mise à jour dans une variable d’object à la fonction suivante.

La fonction finale mettra à jour l’article nouvellement créé avec les identifiants de plate-forme du pipeline précédent.

Quelque chose comme l’exemple suivant:

 var newArticle = {}, platforms = req.body.platforms, date = req.body.date, split = date.split("/"); newArticle.title = req.body.title; newArticle.description = req.body.description; newArticle.date = split[2]+'/'+split[0]+'/'+split[2]; newArticle.link = req.body.link; newArticle.body = req.body.body; console.log(platforms); async.waterfall([ // Create the article function(callback) { var article = new Article(newArticle); article.save(function(err, article){ if (err) return callback(err); callback(null, article); }); }, // Query and update the platforms function(articleData, callback) { var platform_ids = []; async.each(platforms, function(id, callback) { Platform.findByIdAndUpdate(id, { "$push": { "articles": articleData._id } }, { "new": true }, function(err, platform) { if (platform) platform_ids.push(platform._id); callback(err); } ); }, function(err) { // code to run on completion or err if (err) return callback(err); console.log(platform_ids); callback(null, { "article": articleData, "platform_ids": platform_ids }); }); }, // Update the article function(obj, callback) { var article = obj.article; obj.platform_ids.forEach(function(id){ article.platforms.push(id); }); article.save(function(err, article){ if (err) return callback(err); callback(null, article); }); } ], function(err, result) { /* This function gets called after the above tasks have called their "task callbacks" */ if (err) return next(err); console.log(result); res.redirect('articles/' + result._id); }); 

Déplacez votre fonction de sauvegarde

 if(counter==platforms.length){ createdArticle.save(function(err, savedObject){ if(err || !savedObject) console.log(err || "not saved"); else { res.redirect('articles/' + savedObject._id.toSsortingng()); } }); } 

============= EDIT

C’est parce que vous devez appeler article.save une seule fois, et non à chaque boucle. En outre, vous utilisez save () en tant que fonction de synchronisation, mais asynchrone.

Je pense que vous devriez utiliser directement la fonction update:

 } else { var counter=0; // map plateform array id with ObjectID var idarray = platforms.map(function(e){return mongoose.mongo.ObjectID(e);}); // update all plateform with article id Platform.update({_id:{$in: idarray}}, {$push:{articles: createdArticle}}, {multi:true, upsert:false}, function(err, raw){ if(err) { // error case return res.status(403).json({}); } // resortingeve plateform Platform.find({_id:{$in: idarray}}, function(err, results){ if(err || !results) { // error case return res.status(403).json({}); } Article.update({_id: createdArticle._id.toSsortingng()}, {$push:{platforms:{$each: results}}}, {multi:false, upsert:false}, function(err, saved){ if(err || !saved) { // error return res.status(403).json({}); } res.redirect('articles/' + savedObject._id.toSsortingng()); }); }); }); 

Mais c’est une mauvaise idée de stocker des objects complets, pourquoi ne pas stocker uniquement id?