web-dev-qa-db-fra.com

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

Pour le moment, j'utilise save pour ajouter un seul document. Supposons que je souhaite stocker un ensemble de documents sous forme d'objets uniques. Existe-t-il un moyen de tous les ajouter avec un seul appel de fonction, puis d'obtenir un seul rappel une fois l'opération terminée? Je pourrais ajouter tous les documents individuellement, mais la gestion des rappels afin de déterminer quand tout serait terminé serait problématique.

66
Hoa

Mongoose n'a pas encore implémenté d'inserts en vrac (voir numéro # 72 ).

Puisque vous connaissez le nombre de documents que vous enregistrez, vous pouvez é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;//handle error

    result.Push(saved[0]);

    if (--total) saveAll();
    else // all saved here
  })
}

saveAll();

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

36
diversario

Mongoose prend désormais en charge le passage de plusieurs structures de document à Model.create . Pour citer leur exemple d’API, il est possible de transmettre un tableau ou une liste d’objets varargs 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) // ...
});

Modifier: Comme beaucoup l'ont noté, cela n'effectue pas une insertion en bloc mais masque la complexité de l'appel de save à plusieurs reprises . Vous trouverez ci-dessous des réponses et des commentaires expliquant comment utiliser le pilote Mongo réel pour réaliser un insert volumineux dans l’intérêt de la performance.

86
Pascal Zajac

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 n'envoie qu'une seule opération au serveur, plutôt qu'une pour chaque document.

Citant vkarpov15 du numéro # 72 :

Les compromis sont que insertMany () ne déclenche pas de hooks de pré-sauvegarde, mais il devrait avoir de meilleures performances car il ne génère qu'un aller-retour vers la base de données au lieu de 1 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) => {
  ...
})
44
Pier-Luc Gendreau

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

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

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

25
cyberwombat

Utilisez async parallel et votre code ressemblera à ceci:

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

Comme la convention est la même en Mongoose et en async (err, callback), vous n'avez pas besoin de les envelopper dans vos propres rappels, ajoutez simplement vos appels de sauvegarde dans un tableau et vous recevrez un rappel lorsque tout sera terminé.

Si vous utilisez mapLimit, vous pouvez contrôler le nombre de documents que vous souhaitez enregistrer en parallèle. Dans cet exemple, nous enregistrons 10 documents en parallèle jusqu'à ce que tous les éléments soient correctement enregistrés.

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

Je sais que c'est une vieille question, mais cela m'inquiète qu'il n'y ait pas de réponse correctement correcte ici. La plupart des réponses parlent simplement de parcourir tous les documents et de les sauvegarder individuellement, ce qui est une mauvaise idée si vous avez plusieurs documents, et le processus est répété, même pour une demande sur plusieurs.

MongoDB a spécifiquement un appel batchInsert() pour l'insertion de plusieurs documents, qui doit être utilisé à partir du pilote natif mongodb. Mongoose est construit sur ce pilote et ne prend pas en charge les insertions par lots. Cela a probablement du sens car il est supposé être un outil de modélisation de document objet pour MongoDB.

Solution: Mongoose est livré avec le pilote natif MongoDB. Vous pouvez utiliser ce pilote en le nécessitant require('mongoose/node_modules/mongodb') (pas très sûr, mais vous pouvez toujours installer à nouveau mongodb npm si cela ne fonctionne pas, mais je pense que cela devrait être le cas), puis faire un bon batchInsert

8
Munim

Les nouvelles versions de MongoDB prennent en charge les opérations en bloc:

var col = db.collection('people');
var batch = col.initializeUnorderedBulkOp();

batch.insert({name: "John"});
batch.insert({name: "Jane"});
batch.insert({name: "Jason"});
batch.insert({name: "Joanne"});

batch.execute(function(err, result) {
    if (err) console.error(err);
    console.log('Inserted ' + result.nInserted + ' row(s).');
}
7
inxilpro

Voici un autre moyen de ne pas utiliser de bibliothèques supplémentaires (aucune vérification d'erreur incluse)

function saveAll( callback ){
  var count = 0;
  docs.forEach(function(doc){
      doc.save(function(err){
          count++;
          if( count == docs.length ){
             callback();
          }
      });
  });
}
5
mindandmedia

Vous pouvez utiliser la promesse retournée par mangouste save, Promise dans mangouste n'a pas tout, mais vous pouvez ajouter la fonctionnalité à ce module.

Créez un module qui améliore la promesse de la mangouste avec tous.

var Promise = require("mongoose").Promise;

Promise.all = function(promises) {
  var mainPromise = new Promise();
  if (promises.length == 0) {
    mainPromise.resolve(null, promises);
  }

  var pending = 0;
  promises.forEach(function(p, i) {
    pending++;
    p.then(function(val) {
      promises[i] = val;
      if (--pending === 0) {
        mainPromise.resolve(null, promises);
      }
    }, function(err) {
      mainPromise.reject(err);
    });
  });

  return mainPromise;
}

module.exports = Promise;

Puis utilisez-le avec la mangouste:

var Promise = require('./promise')

...

var tasks = [];

for (var i=0; i < docs.length; i++) {
  tasks.Push(docs[i].save());
}

Promise.all(tasks)
  .then(function(results) {
    console.log(results);
  }, function (err) {
    console.log(err);
  })
2
Kristian Benoit

Utilisez la fonction insertMany pour insérer de nombreux documents. Cela envoie une seule opération au serveur et Mongoose valide tous les documents avant de frapper le serveur Mongo. Par défaut, Mongoose insère un élément dans l'ordre dans lequel il existe dans le tableau. Si vous êtes d'accord pour ne maintenir aucun ordre, définissez ordered:false.

Important - Traitement des erreurs:

Quand ordered:true _ la validation et la gestion des erreurs se produisent dans un groupe signifie que si on échoue, tout échouera.

Quand ordered:false _ la validation et la gestion des erreurs se produisent individuellement et l'opération se poursuit. Les erreurs seront rapportées dans un tableau d’erreurs.

1
Praveena

Ajouter un fichier appelé mongoHelper.js

var MongoClient = require('mongodb').MongoClient;

MongoClient.saveAny = function(data, collection, callback)
{
    if(data instanceof Array)
    {
        saveRecords(data,collection, callback);
    }
    else
    {
        saveRecord(data,collection, callback);
    }
}

function saveRecord(data, collection, callback)
{
    collection.save
    (
        data,
        {w:1},
        function(err, result)
        {
            if(err)
                throw new Error(err);
            callback(result);
        }
    );
}
function saveRecords(data, collection, callback)
{
    save
    (
        data, 
        collection,
        callback
    );
}
function save(data, collection, callback)
{
    collection.save
    (
        data.pop(),
        {w:1},
        function(err, result)
        {
            if(err)
            {               
                throw new Error(err);
            }
            if(data.length > 0)
                save(data, collection, callback);
            else
                callback(result);
        }
    );
}

module.exports = MongoClient;

Ensuite, dans votre code, vous devez modifier

var MongoClient = require("./mongoHelper.js");

Puis, quand il est temps de sauvegarder l'appel (après avoir connecté et récupéré la collection)

MongoClient.saveAny(data, collection, function(){db.close();});

Vous pouvez modifier le traitement des erreurs en fonction de vos besoins, renvoyer l'erreur dans le rappel, etc.

0
Eulalie367

C’est une vieille question, mais c’est la première fois que je l'ai trouvée dans les résultats de Google lors de la recherche "mongoose insert array of documents".

Il existe deux options model.create () [mongoose] et model.collection.insert () [mongodb] que vous pouvez utiliser. Voir une discussion plus approfondie ici sur les avantages/inconvénients de chaque option:

insert de lot Mongoose (mongodb)?

0
vbuser2004

Voici un exemple d'utilisation de Model.collection.insert() de MongoDB directement dans Mongoose. Veuillez noter que si vous n'avez pas autant de documents, disons moins de 100, vous n'avez pas besoin d'utiliser l'opération en bloc de MongoDB ( voir ceci ).

MongoDB prend également en charge l’insertion en bloc en passant un tableau de documents à la méthode db.collection.insert ().

var mongoose = require('mongoose');

var userSchema = mongoose.Schema({
  email : { type: String, index: { unique: true } },
  name  : String  
}); 

var User = mongoose.model('User', userSchema);


function saveUsers(users) {
  User.collection.insert(users, function callback(error, insertedDocs) {
    // Here I use KrisKowal's Q (https://github.com/kriskowal/q) to return a promise, 
    // so that the caller of this function can act upon its success or failure
    if (!error)
      return Q.resolve(insertedDocs);
    else
      return Q.reject({ error: error });
  });
}

var users = [{email: '[email protected]', name: 'foo'}, {email: '[email protected]', name: 'baz'}];
saveUsers(users).then(function() {
  // handle success case here
})
.fail(function(error) {
  // handle error case here
});
0
Javad Sadeqzadeh