¿Inserto de lote de mangosta (mongodb)?

114

¿ Mongoose v3.6 + admite inserciones por lotes ahora? He buscado durante unos minutos, pero cualquier cosa que coincida con esta consulta tiene un par de años y la respuesta fue un rotundo no.

Editar:

Para referencia futura, la respuesta es usar Model.create(). create()acepta una matriz como primer argumento, por lo que puede pasar sus documentos para que se inserten como una matriz.

Consulte la documentación de Model.create ()

Geuis
fuente
Vea esta respuesta a una pregunta anterior.
JohnnyHK
Gracias. Eso es lo que terminé encontrando después de publicar.
Geuis
@Geuis, agregue su edición como respuesta y acéptela para resolver su pregunta.
Filip Dupanović
Model.create () es lento y si está considerando insertar una gran cantidad de documentos, es mejor adoptar este enfoque .
Lucio Paiva

Respuestas:

162

Model.create () vs Model.collection.insert (): un enfoque más rápido

Model.create()es una mala forma de hacer inserciones si se trata de un volumen muy grande. Será muy lento . En ese caso debería utilizar Model.collection.insert, que funciona mucho mejor . Dependiendo del tamaño del bulto, ¡ Model.create()incluso se estrellará! Intenté con un millón de documentos, sin suerte. Usarlo Model.collection.inserttomó solo unos segundos.

Model.collection.insert(docs, options, callback)
  • docs es el conjunto de documentos que se insertarán;
  • optionses un objeto de configuración opcional; consulte los documentos
  • callback(err, docs)se llamará después de que se guarden todos los documentos o se produzca un error. En caso de éxito, docs es el conjunto de documentos persistentes.

Como señala aquí el autor de Mongoose , este método evitará cualquier procedimiento de validación y accederá directamente al controlador de Mongo. Es una compensación que debe hacer ya que está manejando una gran cantidad de datos; de lo contrario, no podría insertarlos en su base de datos (recuerde que estamos hablando de cientos de miles de documentos aquí).

Un simple ejemplo

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);
    }
}

Actualización 2019-06-22 : aunque insert()todavía se puede usar sin problemas, ha quedado obsoleto en favor de insertMany(). Los parámetros son exactamente los mismos, por lo que puede usarlo como un reemplazo directo y todo debería funcionar bien (bueno, el valor de retorno es un poco diferente, pero probablemente no lo esté usando de todos modos).

Referencia

Lucio Paiva
fuente
Por favor, dé un ejemplo con Mongoose.
Steve K
15
Dado que Model.collectionpasa directamente por el controlador Mongo, pierde todas las cosas ordenadas de la mangosta, incluida la validación y los ganchos. Sólo algo para tener en cuenta. Model.createpierde los ganchos, pero aún pasa por la validación. Si lo quieres todo, debes iterar ynew MyModel()
Pier-Luc Gendreau
1
@ Pier-LucGendreau Tiene toda la razón, pero es una compensación que debe hacer una vez que comience a trabajar con una enorme cantidad de datos.
Lucio Paiva
1
Tenga cuidado con los nuevos lectores: "Modificado en la versión 2.6: insert () devuelve un objeto que contiene el estado de la operación". No más documentos.
Mark Ni
117

Mongoose 4.4.0 ahora admite inserción a granel

Mongoose 4.4.0 introduce --verdadero-- inserto a granel con el método modelo .insertMany(). Es mucho más rápido que realizar un bucle .create()o proporcionarle una matriz.

Uso:

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

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

O

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

Puedes rastrearlo en:

Derek
fuente
2
En este momento, este método no admite opciones.
Amri
Gracias por la respuesta. ¿Alguna idea de qué análisis de rawDocuments debería estar en su lugar? Lo probé con una matriz de objetos Json y todo lo que insertó fueron solo sus ID. :(
Ondrej Tokar
4
¿En qué se diferencia esto de bulkWrite? Vea aquí: stackoverflow.com/questions/38742475/…
Ondrej Tokar
insertMany no funciona para mí. Tengo un fatal error allocation failed. Pero si uso collection.insert Funciona perfectamente.
John
¿Funcionaría esto con las cosas adicionales que proporciona el esquema de mangosta? ex se este complemento los datos si no existe una fechadateCreated : { type: Date, default: Date.now },
jack en blanco
22

De hecho, puede usar el método "crear" de Mongoose, puede contener una variedad de documentos, vea este ejemplo:

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

La función de devolución de llamada contiene los documentos insertados. No siempre sabe cuántos elementos deben insertarse (longitud de argumento fija como el anterior) para que pueda recorrerlos:

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

Actualización: una mejor solución

Una mejor solución sería usar en Candy.collection.insert()lugar de Candy.create()usar en el ejemplo anterior, porque es más rápido ( create()está llamando Model.save()a cada elemento, por lo que es más lento).

Consulte la documentación de Mongo para obtener más información: http://docs.mongodb.org/manual/reference/method/db.collection.insert/

(gracias a arcseldon por señalar esto)

Benske
fuente
groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds : según lo que desee, el enlace tiene una mejor opción.
arcseldon
¿No te refieres en {type:'jellybean'}lugar de {type:'jelly bean'}? Por cierto. ¿Qué tipos extraños son esos? ¿Son parte de la API de Mongoose?
Steve K
2
Bueno, esa es una mala elección de nombre, ya typeque generalmente está reservado en Mongoose para denominar el ADT de un objeto de base de datos.
Steve K
2
@sirbenbenji Lo cambié, pero era un ejemplo también presente en la documentación oficial. Creo que no fue necesario rechazarlo.
Benske
1
Al abordar la propiedad .collection, está omitiendo Mongoose (validación, métodos 'pre' ...)
Derek
4

Puede realizar una inserción masiva usando mongoDB shell usando la inserción de los valores en una matriz.

db.collection.insert([{values},{values},{values},{values}]);
SUNDARRAJAN K
fuente
¿Hay alguna manera en la mangosta para el inserto a granel?
SUNDARRAJAN K
1
YourModel.collection.insert()
Bill Dami
Al abordar la propiedad .collection, está omitiendo Mongoose (validación, métodos 'pre' ...)
Derek
Esto no es mangosta, y la collection.insertrespuesta en bruto se dio unas semanas antes de esta respuesta y se explicó con mucho más detalle.
Dan Dascalescu
4

Puede realizar una inserción masiva utilizando mangosta, como la respuesta de mayor puntuación. Pero el ejemplo no puede funcionar, debería ser:

/* 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);
    }
}

No use una instancia de esquema para la inserción masiva, debe usar un objeto de mapa simple.

user2582680
fuente
La primera respuesta no es incorrecta, solo tiene validación
Luca Steeb
1
Al abordar la propiedad .collection, está omitiendo Mongoose (validación, métodos 'pre' ...)
Derek
4

Aquí están las dos formas de guardar datos con insertMany y save

1) Mongoose guarda una variedad de documentos a insertManygranel

/* 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 guardar una serie de documentos con .save()

Estos documentos se guardarán en paralelo.

/* 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
fuente
3

Parece que al usar mangosta hay un límite de más de 1000 documentos, cuando se usa

Potato.collection.insert(potatoBag, onInsert);

Puedes usar:

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)
      });
});

Pero esto es casi el doble de rápido cuando se prueba con 10000 documentos:

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
fuente
1
Al abordar la propiedad .collection, está omitiendo Mongoose (validación, métodos 'pre' ...)
Derek
0

Compartiendo código de trabajo y relevante de nuestro proyecto:

//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
fuente
La .insertManysolución ya se dio (y se explicó) en esta respuesta de 2016 .
Dan Dascalescu