現時点では、save を使用して 1 つのドキュメントを追加しています。単一のオブジェクトとして保存したいドキュメントの配列があるとします。単一の関数呼び出しでそれらをすべて追加し、完了時に単一のコールバックを取得する方法はありますか? すべてのドキュメントを個別に追加することもできますが、すべてが完了したときにコールバックを管理するのは問題があります。
13 に答える
Mongoose は、複数のドキュメント構造をModel.createに渡すことをサポートするようになりました。彼らの 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) // ...
});
編集:多くの人が指摘しているように、これは真の一括挿入を実行するものではありませんsave
。自分で複数回呼び出す複雑さを単に隠しているだけです。以下に、実際の Mongo ドライバーを使用してパフォーマンスのために一括挿入を行う方法を説明する回答とコメントがあります。
Mongoose 4.4 は、というメソッドを追加しましたinsertMany
ドキュメントの配列を検証し、それらがすべて有効な場合に MongoDB に挿入するためのショートカット。この関数は、ドキュメントごとに 1 つの操作ではなく、1 つの操作のみをサーバーに送信するため、.create() よりも高速です。
issue #723からの vkarpov15 の引用:
トレードオフは、insertMany() が事前保存フックをトリガーしないことですが、ドキュメントごとに 1 回ではなく、データベースへのラウンドトリップが 1 回しか行われないため、パフォーマンスが向上するはずです。
メソッドのシグネチャは以下と同じですcreate
:
Model.insertMany([ ... ], (err, docs) => {
...
})
または、約束をして:
Model.insertMany([ ... ]).then((docs) => {
...
}).catch((err) => {
...
})
Mongoose にはまだ一括挿入が実装されていません ( 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を使用していますが、これは素晴らしいです)。
ミドルウェアにアクセスする必要がない限り、Mongoose での一括挿入は .insert() で実行できます。
Model.collection.insert(docs, options, callback)
https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L71-91
これが古い質問であることは承知していますが、ここに適切な正解がないことが心配です。ほとんどの回答は、すべてのドキュメントを繰り返し処理し、それぞれを個別に保存することについて話しているだけです。これは、ドキュメントがいくつかある場合は悪い考えであり、多くのリクエストで 1 つでもプロセスが繰り返されます。
MongoDB にはbatchInsert()
複数のドキュメントを挿入するための呼び出しがあり、これはネイティブの mongodb ドライバーから使用する必要があります。Mongoose はこのドライバーに基づいて構築されており、バッチ挿入をサポートしていません。MongoDB のオブジェクト ドキュメント モデリング ツールであることが想定されているため、おそらく理にかなっています。
解決策: Mongoose には、ネイティブの 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();
}
});
});
}
mongoose によって返される promise を使用できます。mongoosesave
に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);
})
Model.collection.insert()
これは、 Mongoose で直接MongoDB を使用する例です。ドキュメントがそれほど多くない場合、たとえば 100 個未満のドキュメントがある場合は、MongoDB の一括操作を使用する必要がないことに注意してください (これを参照してください)。
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: '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
});
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();});
ニーズに合わせてエラー処理を変更したり、コールバックなどでエラーを返すことができます。
これは古い質問ですが、「mongoose insert array of documents」を検索すると、Google の結果で最初に出てきました。
使用できる model.create() [mongoose] と model.collection.insert() [mongodb] の 2 つのオプションがあります。各オプションの長所と短所の詳細については、こちらをご覧ください。