Как я могу сохранить несколько документов одновременно в Мангуст / узел.Джей?


на данный момент я использую сохранить, чтобы добавить один документ. Предположим у меня есть массив документов, которые я хочу хранить как отдельные объекты. Есть ли способ добавить их все с помощью одного вызова функции, а затем получить один обратный вызов, когда это будет сделано? Я мог бы добавить все документы по отдельности, но управление обратными вызовами для разработки, когда все будет сделано, будет проблематичным.

13 58

13 ответов:

Мангуст еще не реализовал массовые вставки (см. выпуск #723).

Так как вы знаете количество документов, которые вы сохраняете, вы можете написать что-то вроде этого:

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

Это, конечно, стоп-зазор решение, и я бы рекомендовал использовать какой-то библиотеки управления потоком (я использую q и это здорово).

Мангуст теперь поддерживает передачу нескольких структур документа модель.создать. Чтобы процитировать их пример API, он поддерживает передачу либо массива, либо списка объектов varargs с обратным вызовом в конце:

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

или

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

Edit: как многие отметили, это не выполняет истинную массовую вставку - она просто скрывает сложность вызова save несколько раз сам. Есть ответы и комментарии ниже объясняя, как использовать фактический драйвер Mongo для достижения объемной вставки в интересах производительности.

Мангуст 4.4 добавлен метод под названием insertMany

ярлык для проверки массива документов и вставки их в MongoDB, если они все действительны. Эта функция работает быстрее .создавать() потому что он отправляет только одну операцию на сервер, а не по одной для каждого документ.

цитирование vkarpov15 из выпуска #723:

компромиссы заключаются в том, что insertMany() не запускает предварительное сохранение крючки, но он должен иметь лучшую производительность, потому что он делает только 1 туда и обратно в базу данных, а не 1 для каждого документа.

сигнатура метода идентична create:

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

или, с обещаниями:

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

Навальные вставки в Мангусте можно сделать С.вставка() если вам нужно получить доступ к промежуточным.

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

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

использовать асинхронный параллельный и ваш код будет выглядеть так:

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

Если вы используете mapLimit вы можете контролировать, сколько документов вы хотите сохранить параллельно. В этом примере мы сохраняем 10 документов параллельно, пока все элементы не будут успешно сохранено.

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

Я знаю, что это старый вопрос, но меня беспокоит, что здесь нет правильных ответов. Большинство ответов просто говорят о повторении всех документов и сохранении каждого из них по отдельности, что является плохой идеей, если у вас есть более нескольких документов, и процесс повторяется даже для одного из многих запросов.

MongoDB специально имеет batchInsert() вызов для вставки нескольких документов, и это должно использоваться из родного драйвера mongodb. Мангуста построен на этом драйвере, и он не имеет поддержки для пакетных вставок. Вероятно, это имеет смысл, поскольку он должен быть инструментом моделирования объектного документа для MongoDB.

решение: Мангуст поставляется с родным драйвером MongoDB. Вы можете использовать этот драйвер, требуя его require('mongoose/node_modules/mongodb') (не слишком уверен в этом, но вы всегда можете установить mongodb npm снова, если он не работает, но я думаю, что он должен) , а затем сделать правильный batchInsert

вот еще один способ без использования дополнительных библиотек (без проверки ошибок)

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

новые версии MongoDB поддерживают массовые операции:

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

вы можете использовать обещание, возвращенный мангуста save,Promise в мангусте есть не все, но вы можете добавить эту функцию с помощью этого модуля.

создайте модуль, который улучшит обещание мангуста со всеми.

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;

затем используйте его с Мангустом:

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

Добавить файл с именем 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;

тогда в вашем коде изменение вам требуется

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

затем, когда пришло время, чтобы сохранить вызов (после подключения и извлечения коллекции)

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

вы можете изменить обработку ошибок в соответствии с вашими потребностями, передать обратно ошибка обратного вызова и т. д.

Это старый вопрос, но он появился первым для меня в результатах google при поиске "Мангуст вставить массив документов".

есть два варианта модели.создать() [мангуст] и модель.коллекция.вставьте () [mongodb], который вы можете использовать. Просмотрите более подробное обсуждение здесь плюсов / минусов каждого варианта:

Мангуст (mongodb) пакетная вставка?

вот пример использования MongoDB в Model.collection.insert() прямо в Мангуста. Обратите внимание, что если у вас не так много документов, скажем, менее 100 документов, вам не нужно использовать массовую операцию MongoDB (посмотреть этот).

MongoDB также поддерживает массовую вставку через передачу массива документы в БД.коллекция.метод 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: 'foo@bar.com', name: 'foo'}, {email: 'baz@bar.com', name: 'baz'}];
saveUsers(users).then(function() {
  // handle success case here
})
.fail(function(error) {
  // handle error case here
});

использовать insertMany функция для вставки многих документов. Это отправляет только одну операцию на сервер и Mongoose проверяет все документы перед попаданием на сервер mongo. По умолчанию Mongoose вставляет элемент в порядке их существования в массиве. Если вы в порядке с не поддержанием любого порядка, то установите ordered:false.

важно - ошибка обработки:

, когда ordered:true проверка и обработка ошибок происходит в группе означает, если не все неудача.

, когда ordered:false проверка и обработка ошибок происходит индивидуально и операция будет продолжена. Ошибка будет сообщена обратно в массиве ошибок.