2012-04-22 61 views
54

目前我使用保存添加單個文檔。假設我有一組我希望作爲單個對象存儲的文檔。有沒有一種方法可以通過單個函數調用添加它們,然後在完成時獲取單個回調?我可以單獨添加所有文檔,但管理回調以確定何時完成所有操作都會產生問題。如何在Mongoose/Node.js中同時保存多個文檔?

+0

您需要控制代碼流使用某種異步庫,如異步。 (有並行功能,當完成回調調用時) – 2012-04-22 08:47:27

+0

https://groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds – arcseldon 2014-07-24 19:18:29

回答

31

貓鼬沒有實現批量插入(請參閱issue #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,它很棒)。

+2

您可以使用q提供解決方案嗎? – Manu 2015-04-22 07:19:34

+3

我不認爲這是「併發」。直到前一個完成後纔會調用每個保存。 – 2015-06-24 00:22:19

+0

是的。例如,一個更加併發的方法是,將所有的「保存」都燒掉,等待所有人調用它們的回調並返回一組結果。您可以使用async或某​​種承諾界面。 – diversario 2015-06-24 04:15:08

6

這裏是不使用額外的庫的另一種方式(檢查包括無差錯)

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

貓鼬不現在支持傳遞多個文件結構,以Model.create。引述他們的API例如,它支持傳遞數組或在年底有一個回調對象的可變參數列表:

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

編輯:正如許多人所指出的,這不會執行真正的批量插入 - 它只是隱藏了自己多次調用save的複雜性。下面的答案和評論解釋瞭如何使用實際的Mongo驅動程序來實現批量插入以實現性能。

+13

注意:這不是一個BULK插入 - 底層的mongoose實現循環遍歷所有元素並逐一提交它們。 – outside2344 2014-05-24 14:08:16

+1

^這是非常相關的,因爲它可能嚴重影響那些使用它的人的表現。 – 2014-06-13 10:06:40

+1

對Aaron Heckman 2011的迴應:並非如此。 Model.create(doc1 [,docN],回調)在這裏有幫助,但它仍然在每一個調用model.save。 如果「快」你的意思是「超越所有的貓鼬掛鉤和驗證」,那麼你可以下降到本地驅動程序,直接使用它: Movie.collection.insert(文檔,期權,回調) HTTPS:/ /github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L96-113 – arcseldon 2014-07-24 19:16:17

0

添加一個名爲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();}); 

您可以更改錯誤處理以滿足您的需要,錯誤回調等

10

使用async parallel和你的代碼看起來就像這樣:

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

因爲公約是貓鼬一樣異步(ERR,回調)你不需要包他們在你自己的回調中,只需將你的保存調用添加到一個數組中,當全部完成時你將得到一個回調。

如果您使用mapLimit,則可以控制要並行保存多少個文檔。在這個例子中,我們並行地保存了10個文檔,直到所有項目都成功保存。

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

有意思 - 你會介意給一個現實世界可用的例子,帶一個'myArray';而myArray擁有1000萬件物品。 – nottinhill 2015-02-24 17:52:00

+1

使用mapLimits更新了一個單獨的示例 – 2015-02-25 15:28:09

+0

非常酷的使用異步! – 2016-02-03 02:04:02

7

我知道這是一個老問題,但我很擔心,有沒有正確的正確答案在這裏。大多數的答案只是談論過的所有文件單獨迭代和保存他們每個人,這是一個壞主意,如果你擁有很多的文件多了,過程被重複,甚至一個在許多請求。

的MongoDB專門有一個batchInsert()呼籲將多個文檔,這應該從本地MongoDB的驅動程序一起使用。 Mongoose建立在此驅動程序上,並且不支持批量插入。這可能是有道理的,因爲它應該是MongoDB的Object文檔建模工具。

解決方案:貓鼬自帶的本地MongoDB的驅動程序。你可以要求它require('mongoose/node_modules/mongodb')使用的驅動程序(也不太清楚這一點,但你總是可以重新安裝MongoDB的NPM,如果它不工作,但我認爲它應該),然後做一個適當的batchInsert

+2

錯誤,帕斯卡的回答完全忽略了這一觀點。需要批量插入的人往往需要它,因爲他們想要一次插入10,000,000個項目。如果沒有批量插入,需要幾秒鐘的操作可能需要幾小時。 Model.create是一個史詩般的失敗,因爲它假裝是一個批量插入,但在引擎蓋下它只是一個for循環。 – user3690202 2014-06-23 01:03:25

+0

然後,貓鼬嚴重需要一些改造。他們的文件也留下了很多希望。 – nottinhill 2014-09-17 15:49:52

+0

我想@Yashua的問題通過使用基礎的'mongodb' javascript驅動來解決它。 – 2014-11-26 18:41:09

24

批量插入在Mongoose中可以用.insert()來完成,除非你需要訪問中間件。

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

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

+1

2011年亞倫赫克曼的迴應:不是真的。 Model.create(doc1 [,docN],回調)在這裏有幫助,但它仍然在每一個調用model.save。 如果「快」你的意思是「超越所有的貓鼬掛鉤和驗證」,那麼你可以下降到本地驅動程序,直接使用它: Movie.collection.insert(文檔,期權,回調) HTTPS:/ /github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L96-113 – arcseldon 2014-07-24 19:14:21

+1

我一直在看這個答案,但這並不是真正的「貓鼬」做事方式。這完全繞過了Mongoose模型。如果您爲貓鼬模型中的某些字段設置了默認值,則它們將被忽略,並且不會插入到數據庫中。 – Nahn 2014-09-04 12:55:17

+1

如何在Mongoose中使用'Model.collection.insert'?請舉一個例子。 – nottinhill 2014-09-17 15:51:06

6

較新版本的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).'); 
} 
2

您可以使用貓鼬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); 
    }) 
0

這是一個老問題,但它在谷歌搜索結果「的文件貓鼬插件組」當走到第一個我。

有兩個選項model.create()[貓鼬]和model.collection.insert()的MongoDB]您可以使用。查看更深入的討論這裏的每個選項的利弊/利弊:

Mongoose (mongodb) batch insert?

0

下面是使用的例子MongoDB的Model.collection.insert()直接在貓鼬。請注意,如果你沒有那麼多文件,即少於100個文件,就不需要使用MongoDB的批量操作(see this)。

MongoDB也通過傳遞的 文檔的陣列到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

哪個貓鼬版本是這個? – R01010010 2016-05-14 02:37:20

25

貓鼬4。4增加了一個名爲insertMany

快捷用於驗證文件的數組,並將其插入到 MongoDB中,如果他們所有的有效方法。此函數比.create()更快,因爲它只向服務器發送一個操作,而不是每個 文檔都發送一個操作。

從問題#723報價vkarpov15:

的權衡是insertMany()不會觸發預存掛鉤,但它應該有更好的表現,因爲它不僅使1往返的數據庫而不是每個文檔1。

方法的簽名是相同的create

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

或者與承諾:

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

謝謝你。它表示如果它們全部有效,它會插入它們;這是否意味着如果一個失敗都會失敗? – Aron 2016-12-15 23:04:39

+0

這是一個批量操作,但它不是原子的。我不確定Mongoose是如何做的,現在不能測試,但它應該返回成功寫入的次數。在MongoDB的文檔中有更多的細節:https://docs.mongodb.com/manual/reference/method/db.collection.insertMany/#error-handling – 2016-12-16 15:23:17

+0

這是完美的和最新的答案!謝謝 – 2017-03-06 09:17:30

0

使用insertMany功能的嵌入式許多文件。這隻會向服務器發送一個操作,並在打到mongo服務器之前驗證所有文檔。默認情況下,Mongoose按照它們在數組中存在的順序插入項目。如果您沒有維護任何訂單,請確定ordered:false

重要 - 錯誤處理:如果一個失敗,一切都將失敗

ordered:true驗證和錯誤處理組中發生手段。

ordered:false驗證和錯誤處理單獨發生並且操作將繼續。錯誤將以一系列錯誤報告。

相關問題