Inserção de lote de Mongoose (mongodb)?


114

O Mongoose v3.6 + suporta inserções em lote agora? Procurei por alguns minutos, mas qualquer coisa que corresponda a essa consulta tem alguns anos e a resposta foi um não inequívoco.

Editar:

Para referência futura, a resposta é usar Model.create(). create()aceita um array como seu primeiro argumento, então você pode passar seus documentos a serem inseridos como um array.

Veja a documentação de Model.create ()


Veja esta resposta a uma pergunta anterior.
JohnnyHK

Obrigado. Foi o que acabei encontrando após postar.
Geuis

@Geuis por favor, adicione sua edição como uma resposta e aceite-a para resolver sua questão.
Filip Dupanović


Model.create () é lento e se você está pensando em inserir um grande número de documentos, é melhor seguir essa abordagem .
Lucio Paiva

Respostas:


162

Model.create () vs Model.collection.insert (): uma abordagem mais rápida

Model.create()é uma maneira ruim de fazer inserções se você estiver lidando com um volume muito grande. Vai ser muito lento . Nesse caso, você deve usar Model.collection.insert, que tem um desempenho muito melhor . Dependendo do tamanho do bulk, Model.create()vai até travar! Tentei com um milhão de documentos, sem sorte. Usá- Model.collection.insertlo demorou apenas alguns segundos.

Model.collection.insert(docs, options, callback)
  • docs é a matriz de documentos a serem inseridos;
  • optionsé um objeto de configuração opcional - veja a documentação
  • callback(err, docs)será chamado depois que todos os documentos forem salvos ou ocorrer um erro. Em caso de sucesso, docs é a matriz de documentos persistentes.

Como o autor do Mongoose aponta aqui , este método irá ignorar qualquer procedimento de validação e acessar o driver Mongo diretamente. É uma troca que você tem que fazer, já que está lidando com uma grande quantidade de dados, caso contrário, você não seria capaz de inseri-los em seu banco de dados (lembre-se de que estamos falando de centenas de milhares de documentos aqui).

Um exemplo simples

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

Atualização 22/06/2019 : embora insert()ainda possa ser usado sem problemas, ele foi substituído por insertMany(). Os parâmetros são exatamente os mesmos, então você pode apenas usá-lo como um substituto drop-in e tudo deve funcionar bem (bem, o valor de retorno é um pouco diferente, mas provavelmente você não o está usando de qualquer maneira).

Referência



Por favor, dê um exemplo com o Mongoose.
Steve K,

15
Como Model.collectionpassa diretamente pelo driver do Mongo, você perde todas as coisas legais do mangusto, incluindo validação e ganchos. Apenas algo para ter em mente. Model.createperde os ganchos, mas ainda passa pela validação. Se você quiser tudo, deve iterar enew MyModel()
Pier-Luc Gendreau

1
@ Pier-LucGendreau Você está absolutamente certo, mas é uma troca que você precisa fazer assim que começar a lidar com uma quantidade enorme de dados.
Lucio Paiva

1
Cuidado com os novos leitores: "Alterado na versão 2.6: O insert () retorna um objeto que contém o status da operação". Não há mais documentos.
Mark Ni

117

Mongoose 4.4.0 agora suporta inserção em massa

O Mongoose 4.4.0 apresenta a inserção --true-- bulk com o método model .insertMany(). É muito mais rápido do que fazer um loop .create()ou fornecer um array.

Uso:

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

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

Ou

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

Você pode rastreá-lo em:


2
No momento, esse método não oferece suporte a opções.
Amri

Obrigado pela resposta. Alguma ideia de qual análise de rawDocuments deve ser feita? Eu tentei com uma matriz de objetos Json e tudo o que inseriu foram apenas seus IDs. :(
Ondrej Tokar

4
Como isso é diferente de bulkWrite? Veja aqui: stackoverflow.com/questions/38742475/…
Ondrej Tokar

insertMany não funciona para mim. Eu tenho um fatal error allocation failed. Mas se eu usar collection.insert Funciona perfeitamente.
John

Isso funcionaria com o material extra que o esquema mongoose fornece? por exemplo, isso adicionará os dados se não houver datadateCreated : { type: Date, default: Date.now },
jack blank

22

Na verdade, você pode usar o método "criar" do Mongoose, ele pode conter uma série de documentos, veja este exemplo:

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

A função de retorno de chamada contém os documentos inseridos. Você nem sempre sabe quantos itens devem ser inseridos (comprimento de argumento fixo como acima) para que possa percorrê-los:

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

Atualização: uma solução melhor

Uma solução melhor seria usar em Candy.collection.insert()vez de Candy.create()- usado no exemplo acima - porque é mais rápido ( create()chama Model.save()cada item, então é mais lento).

Consulte a documentação do Mongo para obter mais informações: http://docs.mongodb.org/manual/reference/method/db.collection.insert/

(obrigado a arcseldon por apontar isso)


groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds - Dependendo do que você deseja, o link tem uma opção melhor.
arcseldon

Você não quer dizer em {type:'jellybean'}vez de {type:'jelly bean'}? Btw. que tipos estranhos são esses? Eles fazem parte da API Mongoose?
Steve K de

2
Bem, essa é uma escolha de nomenclatura ruim, pois typegeralmente é reservado no Mongoose para denominar o ADT de um objeto de banco de dados.
Steve K de

2
@sirbenbenji Eu mudei, mas era um exemplo também presente na documentação oficial. Não foi necessário fazer um downvote para isso, eu acho.
benske

1
Ao abordar a propriedade .collection, você está evitando o Mongoose (validação, métodos 'pré' ...)
Derek

4

Você pode realizar a inserção em massa usando o shell mongoDB, inserindo os valores em uma matriz.

db.collection.insert([{values},{values},{values},{values}]);

existe uma maneira no mangusto para inserção em massa?
SUNDARRAJAN K

1
YourModel.collection.insert()
Bill Dami

Ao abordar a propriedade .collection, você está evitando o Mongoose (validação, métodos 'pré' ...)
Derek

Este não é um mangusto, e a collection.insertresposta bruta foi dada algumas semanas antes desta resposta e explicada com muito mais detalhes.
Dan Dascalescu

4

Você pode realizar a inserção em massa usando o mangusto, como a resposta de maior pontuação. Mas o exemplo não pode funcionar, deveria 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);
    }
}

Não use uma instância de esquema para a inserção em massa, você deve usar um objeto de mapa simples.


A primeira resposta não está errada, apenas tem validação
Luca Steeb

1
Ao abordar a propriedade .collection, você está evitando o Mongoose (validação, métodos 'pré' ...)
Derek

4

Aqui estão as duas maneiras de salvar dados com insertMany e salvar

1) Mongoose salvar matriz de documentos com insertManyem massa

/* 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 salvar matriz de documentos com .save()

Esses documentos serão salvos paralelamente.

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

3

Parece que usando o mangusto há um limite de mais de 1000 documentos, ao usar

Potato.collection.insert(potatoBag, onInsert);

Você pode 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)
      });
});

Mas isso é quase duas vezes mais rápido ao testar com 10.000 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);
    });
}

1
Ao abordar a propriedade .collection, você está evitando o Mongoose (validação, métodos 'pré' ...)
Derek

0

Compartilhando código funcional e relevante de nosso projeto:

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

A .insertManysolução já foi dada (e explicada) nesta resposta de 2016 .
Dan Dascalescu
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.