有没有选项用mongoose执行批量upserts?所以基本上有一个数组并插入每个元素,如果它不存在或更新它,如果它存在? (我正在使用海关_ids)
当我使用 .insert 时,MongoDB会为重复键返回错误E11000(应该更新)。插入多个新文档可以正常工作:
var Users = self.db.collection('Users');
Users.insert(data, function(err){
if (err) {
callback(err);
}
else {
callback(null);
}
});
使用 .save 会返回错误,该参数必须是单个文档:
Users.save(data, function(err){
...
}
This answer建议没有这样的选择,但是它特定于C#并且已经有3年了。所以我想知道是否有任何选择使用mongoose吗?
谢谢!
答案 0 :(得分:22)
不在" mongoose"特别地,或者至少还没有写作。从2.6版开始的MongoDB shell实际上使用了"Bulk operations API""引擎盖下的#34;因为它适用于所有一般辅助方法。在它的实现中,它首先尝试执行此操作,如果检测到旧版本服务器,则会出现" fallback"遗留实施。
所有猫鼬方法"目前"使用"遗产"实现或写入关注响应和基本遗留方法。但是,任何给定的mongoose模型都有一个.collection
访问器,它基本上可以访问"集合对象"来自底层"节点本机驱动程序"在哪个猫鼬实现自己:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var sampleSchema = new Schema({},{ "strict": false });
var Sample = mongoose.model( "Sample", sampleSchema, "sample" );
mongoose.connection.on("open", function(err,conn) {
var bulk = Sample.collection.initializeOrderedBulkOp();
var counter = 0;
// representing a long loop
for ( var x = 0; x < 100000; x++ ) {
bulk.find(/* some search */).upsert().updateOne(
/* update conditions */
});
counter++;
if ( counter % 1000 == 0 )
bulk.execute(function(err,result) {
bulk = Sample.collection.initializeOrderedBulkOp();
});
}
if ( counter % 1000 != 0 )
bulk.execute(function(err,result) {
// maybe do something with result
});
});
主要有这样的&#34; mongoose方法&#34;实际上已经意识到实际上还没有建立连接并且#34;队列&#34;直到完成。你是本土的司机&#34;挖掘&#34;没有做出这种区分。
所以你必须要知道连接是以某种方式或形式建立的。但是只要你小心处理你正在做的事情,就可以使用本机驱动程序方法。
答案 1 :(得分:18)
您不需要像@ neil-lunn建议那样管理限制(1000)。 Mongoose已经这样做了。我用他的好答案作为完整的基于Promise的实现的基础。例如:
var Promise = require('bluebird');
var mongoose = require('mongoose');
var Show = mongoose.model('Show', {
"id": Number,
"title": String,
"provider": {'type':String, 'default':'eztv'}
});
/**
* Atomic connect Promise - not sure if I need this, might be in mongoose already..
* @return {Priomise}
*/
function connect(uri, options){
return new Promise(function(resolve, reject){
mongoose.connect(uri, options, function(err){
if (err) return reject(err);
resolve(mongoose.connection);
});
});
}
/**
* Bulk-upsert an array of records
* @param {Array} records List of records to update
* @param {Model} Model Mongoose model to update
* @param {Object} match Database field to match
* @return {Promise} always resolves a BulkWriteResult
*/
function save(records, Model, match){
match = match || 'id';
return new Promise(function(resolve, reject){
var bulk = Model.collection.initializeUnorderedBulkOp();
records.forEach(function(record){
var query = {};
query[match] = record[match];
bulk.find(query).upsert().updateOne( record );
});
bulk.execute(function(err, bulkres){
if (err) return reject(err);
resolve(bulkres);
});
});
}
/**
* Map function for EZTV-to-Show
* @param {Object} show EZTV show
* @return {Object} Mongoose Show object
*/
function mapEZ(show){
return {
title: show.title,
id: Number(show.id),
provider: 'eztv'
};
}
// if you are not using EZTV, put shows in here
var shows = []; // giant array of {id: X, title: "X"}
// var eztv = require('eztv');
// eztv.getShows({}, function(err, shows){
// if(err) return console.log('EZ Error:', err);
// var shows = shows.map(mapEZ);
console.log('found', shows.length, 'shows.');
connect('mongodb://localhost/tv', {}).then(function(db){
save(shows, Show).then(function(bulkRes){
console.log('Bulk complete.', bulkRes);
db.close();
}, function(err){
console.log('Bulk Error:', err);
db.close();
});
}, function(err){
console.log('DB Error:', err);
});
// });
这可以在完成连接时关闭连接,如果你关心则显示任何错误,但如果没有则忽略它们(Promises中的错误回调是可选的。)它也非常快。离开这里分享我的发现。例如,如果要将所有eztv节目保存到数据库,可以取消注释eztv的内容。
答案 2 :(得分:3)
我已经发布了一个Mongoose插件,它公开了一个静态upsertMany
方法,用promise接口执行批量upsert操作。
使用此插件而不是在底层集合上初始化自己的批量操作,这个插件的一个额外好处是,此插件首先将数据转换为Mongoose模型,然后在upsert之前返回到普通对象。这样可以确保应用Mongoose模式验证,并且数据将减少并适合原始插入。
https://github.com/meanie/mongoose-upsert-many https://www.npmjs.com/package/@meanie/mongoose-upsert-many
希望它有所帮助!
答案 3 :(得分:3)
await Model.bulkWrite(docs.map(doc => ({
updateOne: {
filter: {id: doc.id},
update: doc,
upsert: true
}
})))
或更详细:
const bulkOps = docs.map(doc => ({
updateOne: {
filter: {id: doc.id},
update: doc,
upsert: true
}
}))
Model.bulkWrite(bulkOps)
.then(bulkWriteOpResult => console.log('BULK update OK:', bulkWriteOpResult))
.catch(err => console.error('BULK update error:', err))
答案 4 :(得分:1)
如果您没有在db.collection中看到批量方法,那么您将收到错误信息 xxx变量没有方法:initializeOrderedBulkOp()
尝试更新您的猫鼬版本。显然,较旧的mongoose版本不会通过所有底层的mongo db.collection方法。
npm install mongoose
为我照顾它。
答案 5 :(得分:0)
我最近在将电子商务应用中的产品存储时必须实现这一目标。我的数据库用于超时,因为我必须每4小时插入10000个项目。对我来说,一个选择是在连接到数据库的同时在mongoose中设置socketTimeoutMS和connectTimeoutMS但是它感觉很糟糕,我不想操纵数据库的连接超时默认值。我还看到@neil lunn的解决方案采用了一种简单的同步方法,即在for循环中获取模数。这是我的异步版本,我相信这项工作要好得多
$2, $1$3
答案 6 :(得分:0)
你可以使用猫鼬的 Model.bulkWrite()
const res = await Character.bulkWrite([
{
updateOne: {
filter: { name: 'Will Riker' },
update: { age: 29 },
upsert: true
}
},
{
updateOne: {
filter: { name: 'Geordi La Forge' },
update: { age: 29 },
upsert: true
}
}
]);