103 votes

Comment puis-je enregistrer plusieurs documents simultanément dans Mongoose/Node.js?

Actuellement, j'utilise la méthode save pour ajouter un seul document. Supposons que j'ai un tableau de documents que je souhaite stocker en tant qu'objets individuels. Existe-t-il un moyen de les ajouter tous avec un seul appel de fonction et ensuite d'obtenir un seul rappel lorsque c'est fait? Je pourrais ajouter tous les documents individuellement, mais gérer les rappels pour savoir quand tout est terminé serait problématique.

0 votes

Vous devez contrôler le flux de code en utilisant une sorte de bibliothèque asynchrone comme async. (il y a une fonction parallèle et une fois terminée, le rappel est appelé)

0 votes

104voto

Pascal Zajac Points 1087

Mongoose supporte désormais le passage de structures de documents multiples à Model.create. Pour citer leur exemple d'API, il supporte le passage d'un tableau ou d'une liste varargs d'objets avec un rappel à la fin :

Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
    if (err) // ...
});

Ou

var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
Candy.create(array, function (err, jellybean, snickers) {
    if (err) // ...
});

Éditer : Comme beaucoup l'ont remarqué, cela ne réalise pas une véritable insertion en masse - cela cache simplement la complexité d'appeler save plusieurs fois vous-même. Il y a des réponses et des commentaires ci-dessous expliquant comment utiliser le vrai pilote Mongo pour réaliser une insertion en masse dans l'intérêt de la performance.

20 votes

Note : il ne s'agit pas d'une insertion en bloc - l'implémentation sous-jacente de mongoose parcourt tous les éléments et les valide un par un.

1 votes

^ cela est très pertinent, car cela pourrait sérieusement affecter les performances de ceux qui l'utilisent intensivement.

1 votes

Réponse pour Aaron Heckman 2011: pas vraiment. Model.create(doc1 [, docN], callback) aide un peu ici mais il appelle toujours model.save pour vous sur chaque élément. Si par "plus rapide" vous entendez "contourner tous les hooks et la validation de mongoose" alors vous pourriez passer au pilote natif et l'utiliser directement: Movie.collection.insert(docs, options, callback) github.com/christkv/node-mongodb-native/blob/master/lib/mong‌​odb/…

88voto

Pier-Luc Gendreau Points 2288

Mongoose 4.4 a ajouté une méthode appelée insertMany

Raccourci pour valider un tableau de documents et les insérer dans MongoDB s'ils sont tous valides. Cette fonction est plus rapide que .create() car elle envoie une seule opération au serveur, plutôt qu'une pour chaque document.

En citant vkarpov15 de l'issue #723:

Les compromis sont que insertMany() ne déclenche pas les hooks avant l'enregistrement, mais il devrait avoir de meilleures performances car il ne fait qu'un aller-retour avec la base de données au lieu d'un pour chaque document.

La signature de la méthode est identique à create:

Model.insertMany([ ... ], (err, docs) => {
  ...
})

Ou, avec des promesses :

Model.insertMany([ ... ]).then((docs) => {
  ...
}).catch((err) => {
  ...
})

0 votes

Merci pour cela. Il dit qu'il les insérera s'ils sont tous valides; cela signifie-t-il que s'il échoue, tous échoueront aussi?

1 votes

Il s'agit d'une opération en masse, mais elle n'est pas atomique. Je ne suis pas sûr de la manière dont Mongoose le fait et je ne peux pas le tester en ce moment, mais cela devrait retourner le nombre d'écritures réussies. Vous trouverez plus de détails dans la documentation de MongoDB : docs.mongodb.com/manual/reference/method/…

10 votes

Si l'un échoue, insertMany n'insère rien, j'ai effectué les tests.

44voto

diversario Points 500

Mongoose n'a pas encore implémenté les inserts en vrac (voir problème n°723).

Comme vous connaissez le nombre de documents que vous enregistrez, vous pourriez écrire quelque chose comme ceci :

var total = docArray.length
  , result = []
;

function saveAll(){
  var doc = docArray.pop();

  doc.save(function(err, saved){
    if (err) throw err;//gérer l'erreur

    result.push(saved[0]);

    if (--total) saveAll();
    else // tout est enregistré ici
  })
}

saveAll();

Ceci, bien sûr, est une solution provisoire et je recommanderais d'utiliser une sorte de bibliothèque de contrôle de flux (j'utilise q et c'est génial).

2 votes

Pouvez-vous fournir la solution en utilisant la balise q s'il vous plaît ?

9 votes

Je ne pense pas que cela soit "concurrent". Chaque enregistrement n'est pas invoqué tant que le précédent n'est pas terminé.

0 votes

Vrai. Une approche plus concurrente pourrait être, par exemple, de déclencher tous les save, d'attendre que tous appellent leur rappel et de renvoyer un tableau de résultats. Vous pourriez utiliser async pour cela, ou une interface de promesse.

27voto

Yashua Points 3482

Les insertions en masse dans Mongoose peuvent être effectuées avec .insert() sauf si vous avez besoin d'accéder au middleware.

Model.collection.insert(docs, options, callback)

https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L71-91

1 votes

Réponse pour Aaron Heckman 2011 : pas vraiment. Model.create(doc1 [, docN], callback) aide un peu ici mais appelle toujours model.save pour vous sur chacun. Si par "plus rapide" vous voulez dire "contourner tous les hooks et les validations de mongoose" alors vous pourriez passer au pilote natif et l'utiliser directement : Movie.collection.insert(docs, options, callback) github.com/christkv/node-mongodb-native/blob/master/lib/mong‌​odb/…

1 votes

Je continue de voir cette réponse, mais ce n'est pas vraiment une façon de faire "mongoose". Cela contourne complètement les modèles Mongoose. Si vous avez des valeurs par défaut définies pour certains champs dans les modèles mongoose, elles seront ignorées et non insérées dans la DB.

1 votes

Comment utiliser Model.collection.insert dans Mongoose ? Veuillez fournir un exemple.

18voto

Christian Landgren Points 1127

Utilisez async parallel et votre code ressemblera à ceci :

  async.parallel([obj1.save, obj2.save, obj3.save], callback);

Comme la convention est la même dans Mongoose que dans async (err, callback), vous n'avez pas besoin de les envelopper dans vos propres callbacks, il vous suffit d'ajouter vos appels save dans un tableau et vous obtiendrez un callback lorsque tout est terminé.

Si vous utilisez mapLimit, vous pouvez contrôler le nombre de documents que vous souhaitez sauvegarder en parallèle. Dans cet exemple, nous sauvegardons 10 documents en parallèle jusqu'à ce que tous les éléments soient sauvegardés avec succès.

async.mapLimit(myArray, 10, function(document, next){
  document.save(next);
}, done);

2 votes

Intéressant - auriez-vous l'obligeance de donner un exemple utilisable dans le monde réel avec un myArray; alors que myArray contient 10 millions d'éléments.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X