Insertion de lots de mangouste (mongodb)?

114

Est-ce que Mongoose v3.6 + inserts de traitement par lots de soutien maintenant? J'ai cherché pendant quelques minutes, mais tout ce qui correspond à cette requête date de quelques années et la réponse était un non sans équivoque.

Éditer:

Pour référence future, la réponse est d'utiliser Model.create(). create()accepte un tableau comme premier argument, vous pouvez donc transmettre vos documents à insérer sous forme de tableau.

Voir la documentation Model.create ()

Geuis
la source
Voir cette réponse à une question précédente.
JohnnyHK
Merci. C'est ce que j'ai fini par trouver après avoir posté.
Geuis
@Geuis, veuillez ajouter votre modification comme réponse et l'accepter pour résoudre votre question.
Filip Dupanović
Model.create () est lent et si vous envisagez d'insérer un grand nombre de documents, il vaut mieux adopter cette approche à la place.
Lucio Paiva

Réponses:

162

Model.create () vs Model.collection.insert (): une approche plus rapide

Model.create()est une mauvaise façon de faire des insertions si vous avez affaire à un très gros volume. Ce sera très lent . Dans ce cas, vous devez utiliserModel.collection.insert , qui fonctionne beaucoup mieux . Selon la taille de la masse, Model.create()va même planter! Essayé avec un million de documents, pas de chance. Son utilisation Model.collection.insertn'a pris que quelques secondes.

Model.collection.insert(docs, options, callback)
  • docs est le tableau des documents à insérer;
  • optionsest un objet de configuration facultatif - voir la documentation
  • callback(err, docs)sera appelé une fois que tous les documents auront été enregistrés ou qu'une erreur se produira. En cas de succès, docs est le tableau des documents persistants.

Comme l'auteur de Mongoose le souligne ici , cette méthode contournera toutes les procédures de validation et accédera directement au pilote Mongo. C'est un compromis que vous devez faire puisque vous manipulez une grande quantité de données, sinon vous ne pourriez pas du tout l'insérer dans votre base de données (rappelez-vous que nous parlons ici de centaines de milliers de documents).

Un exemple simple

var Potato = mongoose.model('Potato', PotatoSchema);

var potatoBag = [/* a humongous amount of potato objects */];

Potato.collection.insert(potatoBag, onInsert);

function onInsert(err, docs) {
    if (err) {
        // TODO: handle error
    } else {
        console.info('%d potatoes were successfully stored.', docs.length);
    }
}

Mise à jour 2019-06-22 : bien queinsert() puisse toujours être utilisée très bien, elle est obsolète au profit de insertMany(). Les paramètres sont exactement les mêmes, vous pouvez donc simplement l'utiliser comme un remplacement instantané et tout devrait fonctionner correctement (enfin, la valeur de retour est un peu différente, mais vous ne l'utilisez probablement pas de toute façon).

Référence

Lucio Paiva
la source
Veuillez donner un exemple avec Mongoose.
Steve K le
15
Puisque Model.collectionpasse directement par le pilote Mongo, vous perdez tout le truc de mangouste, y compris la validation et les crochets. Juste quelque chose à garder à l'esprit. Model.createperd les crochets, mais passe toujours par la validation. Si vous voulez tout, vous devez itérer etnew MyModel()
Pier-Luc Gendreau
1
@ Pier-LucGendreau Vous avez tout à fait raison, mais c'est un compromis que vous devez faire une fois que vous commencez à traiter une énorme quantité de données.
Lucio Paiva
1
Attention aux nouveaux lecteurs: "Modifié dans la version 2.6: L'insert () renvoie un objet qui contient l'état de l'opération". Plus de documentation.
Mark Ni
117

Mongoose 4.4.0 prend désormais en charge l'insertion en masse

Mongoose 4.4.0 introduit --true-- insertion en bloc avec la méthode model .insertMany(). C'est beaucoup plus rapide que de boucler .create()ou de lui fournir un tableau.

Usage:

var rawDocuments = [/* ... */];

Book.insertMany(rawDocuments)
    .then(function(mongooseDocuments) {
         /* ... */
    })
    .catch(function(err) {
        /* Error handling */
    });

Ou

Book.insertMany(rawDocuments, function (err, mongooseDocuments) { /* Your callback function... */ });

Vous pouvez le suivre sur:

Derek
la source
2
Pour le moment, cette méthode ne prend pas en charge les options.
Amri
Merci pour la réponse. Une idée de ce que l'analyse des rawDocuments devrait être en place? Je l'ai essayé avec un tableau d'objets Json et tout ce qu'il a inséré était juste leurs identifiants. :(
Ondrej Tokar
4
En quoi est-ce différent bulkWrite? Voir ici: stackoverflow.com/questions/38742475/…
Ondrej Tokar
insertMany ne fonctionne pas pour moi. J'ai un fatal error allocation failed. Mais si j'utilise collection.insert Cela fonctionne parfaitement.
John
Cela fonctionnerait-il avec les éléments supplémentaires fournis par le schéma de mangouste? ex est-ce que cela ajoutera les données s'il n'y a pas de datedateCreated : { type: Date, default: Date.now },
jack vide
22

En effet, vous pouvez utiliser la méthode "create" de Mongoose, elle peut contenir un tableau de documents, voir cet exemple:

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

La fonction de rappel contient les documents insérés. Vous ne savez pas toujours combien d'éléments doivent être insérés (longueur d'argument fixe comme ci-dessus) pour pouvoir les parcourir en boucle:

var insertedDocs = [];
for (var i=1; i<arguments.length; ++i) {
    insertedDocs.push(arguments[i]);
}

Mise à jour: une meilleure solution

Une meilleure solution serait d'utiliser Candy.collection.insert()au lieu de Candy.create()- utilisé dans l'exemple ci-dessus - car c'est plus rapide ( create()appelle Model.save()chaque élément donc il est plus lent).

Consultez la documentation Mongo pour plus d'informations: http://docs.mongodb.org/manual/reference/method/db.collection.insert/

(merci à arcseldon pour l'avoir signalé)

Benske
la source
groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds - Selon ce que vous voulez, le lien a une meilleure option.
arcseldon
Tu ne veux pas dire {type:'jellybean'}au lieu de {type:'jelly bean'}? Btw. de quels types étranges s'agit-il? Font-ils partie de l'API Mongoose?
Steve K du
2
Eh bien, c'est un mauvais choix de dénomination, car type est généralement réservé dans Mongoose pour dénommer l'ADT d'un objet de base de données.
Steve K
2
@sirbenbenji Je l'ai changé, mais c'était un exemple également présent dans la documentation officielle. Il n'était pas nécessaire de voter pour cela, je pense.
benske
1
En adressant la propriété .collection, vous contournez Mongoose (validation, méthodes 'pré' ...)
Derek
4

Vous pouvez effectuer une insertion en bloc à l'aide du shell mongoDB en insérant les valeurs dans un tableau.

db.collection.insert([{values},{values},{values},{values}]);
SUNDARRAJAN K
la source
y a-t-il un moyen de mangouste pour l'insert en vrac?
SUNDARRAJAN K
1
YourModel.collection.insert()
Bill Dami
En adressant la propriété .collection, vous contournez Mongoose (validation, méthodes 'pré' ...)
Derek
Ce n'est pas de la mangouste, et la collection.insertréponse brute a été donnée quelques semaines avant cette réponse, et expliquée beaucoup plus en détail.
Dan Dascalescu le
4

Vous pouvez effectuer une insertion en masse en utilisant la mangouste, comme réponse avec le score le plus élevé. Mais l'exemple ne peut pas fonctionner, il devrait être:

/* a humongous amount of potatos */
var potatoBag = [{name:'potato1'}, {name:'potato2'}];

var Potato = mongoose.model('Potato', PotatoSchema);
Potato.collection.insert(potatoBag, onInsert);

function onInsert(err, docs) {
    if (err) {
        // TODO: handle error
    } else {
        console.info('%d potatoes were successfully stored.', docs.length);
    }
}

N'utilisez pas d'instance de schéma pour l'insertion en bloc, vous devez utiliser un objet de carte simple.

user2582680
la source
La première réponse n'est pas fausse, elle a juste une validation
Luca Steeb
1
En adressant la propriété .collection, vous contournez Mongoose (validation, méthodes 'pré' ...)
Derek
4

Voici les deux façons de sauvegarder des données avec insertMany et save

1) Mongoose enregistre un tableau de documents avec insertManyen vrac

/* write mongoose schema model and export this */
var Potato = mongoose.model('Potato', PotatoSchema);

/* write this api in routes directory  */
router.post('/addDocuments', function (req, res) {
    const data = [/* array of object which data need to save in db */];

    Potato.insertMany(data)  
    .then((result) => {
            console.log("result ", result);
            res.status(200).json({'success': 'new documents added!', 'data': result});
    })
    .catch(err => {
            console.error("error ", err);
            res.status(400).json({err});
    });
})

2) Mongoose enregistre un tableau de documents avec .save()

Ces documents enregistreront en parallèle.

/* write mongoose schema model and export this */
var Potato = mongoose.model('Potato', PotatoSchema);

/* write this api in routes directory  */
router.post('/addDocuments', function (req, res) {
    const saveData = []
    const data = [/* array of object which data need to save in db */];
    data.map((i) => {
        console.log(i)
        var potato = new Potato(data[i])
        potato.save()
        .then((result) => {
            console.log(result)
            saveData.push(result)
            if (saveData.length === data.length) {
                res.status(200).json({'success': 'new documents added!', 'data': saveData});
            }
        })
        .catch((err) => {
            console.error(err)
            res.status(500).json({err});
        })
    })
})
Arpit
la source
3

Il semble que l'utilisation de la mangouste soit limitée à plus de 1000 documents, lors de l'utilisation

Potato.collection.insert(potatoBag, onInsert);

Vous pouvez utiliser:

var bulk = Model.collection.initializeOrderedBulkOp();

async.each(users, function (user, callback) {
    bulk.insert(hash);
}, function (err) {
    var bulkStart = Date.now();
    bulk.execute(function(err, res){
        if (err) console.log (" gameResult.js > err " , err);
        console.log (" gameResult.js > BULK TIME  " , Date.now() - bulkStart );
        console.log (" gameResult.js > BULK INSERT " , res.nInserted)
      });
});

Mais c'est presque deux fois plus rapide lors des tests avec 10000 documents:

function fastInsert(arrOfResults) {
var startTime = Date.now();
    var count = 0;
    var c = Math.round( arrOfResults.length / 990);

    var fakeArr = [];
    fakeArr.length = c;
    var docsSaved = 0

    async.each(fakeArr, function (item, callback) {

            var sliced = arrOfResults.slice(count, count+999);
            sliced.length)
            count = count +999;
            if(sliced.length != 0 ){
                    GameResultModel.collection.insert(sliced, function (err, docs) {
                            docsSaved += docs.ops.length
                            callback();
                    });
            }else {
                    callback()
            }
    }, function (err) {
            console.log (" gameResult.js > BULK INSERT AMOUNT: ", arrOfResults.length, "docsSaved  " , docsSaved, " DIFF TIME:",Date.now() - startTime);
    });
}
Ddennis
la source
1
En adressant la propriété .collection, vous contournez Mongoose (validation, méthodes 'pré' ...)
Derek
0

Partage du code fonctionnel et pertinent de notre projet:

//documentsArray is the list of sampleCollection objects
sampleCollection.insertMany(documentsArray)  
    .then((res) => {
        console.log("insert sampleCollection result ", res);
    })
    .catch(err => {
        console.log("bulk insert sampleCollection error ", err);
    });
Zameer
la source
La .insertManysolution était déjà donnée (et expliquée) dans cette réponse de 2016 .
Dan Dascalescu le