尝试使用Mongoose进行批量upsert.最干净的方法是什么?

lfk*_*wtz 15 mongoose mongodb node.js

我有一个包含三个字段的文档的集合:first_name,last_name和age.我试图找出Mongoose中我可以使用什么查询进行批量upsert.我的应用程序偶尔会收到具有相同三个字段的新对象数组.我希望查询检查文档中是否已存在第一个和最后一个名称,如果它们存在,则更新年龄(如果它不同).否则,如果名字和姓氏不存在,请插入新文档.

目前,我只是在进行导入 - 还没有为这个upsert片段构建逻辑.

app.post('/users/import', function(req, res) {
  let data = req.body;
  let dataArray = [];
  data.forEach(datum => {
    dataArray.push({
        first: datum.first,
        last: datum.last,
        age: datum.age
    })
})

User.insertMany(dataArray, answer => {
    console.log(`Data Inserted:`,answer)
})
Run Code Online (Sandbox Code Playgroud)

`

我的用户模型如下所示:

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const userSchema = new Schema({
  first: String,
  last: String,
  age: Number,
  created_at: { type: Date, default: Date.now }
});

var User = mongoose.model('User', userSchema);
module.exports = User;
Run Code Online (Sandbox Code Playgroud)

mag*_*nap 25

(mongoose@4.9.1,mongodb@3.4.2)

在使用Mongoose API糟糕的文档后,我解决了该方法中的批量upsert调整updateOne:{}操作bulkWrite().

一些未记载的事项需要考虑:

// suppose:
var GasStation = mongoose.model('gasstation', gasStationsSchema);
var bulkOps = [ ];

// for ( ... each gasStation to upsert ...) {
  let gasStation = { country:'a', localId:'b', xyz:'c' };
  // [populate gasStation as needed]
  // Each document should look like this: (note the 'upsert': true)
  let upsertDoc = {
    'updateOne': {
      'filter': { 'country': gasStation.country, 'localId': gasStation.localId },
      'update': gasStation,
      'upsert': true
    }};
  bulkOps.push(upsertDoc);
// end for loop

// now bulkWrite (note the use of 'Model.collection')
GasStation.collection.bulkWrite(bulkOps)
  .then( bulkWriteOpResult => {
    console.log('BULK update OK');
    console.log(JSON.stringify(bulkWriteOpResult, null, 2));
  })
  .catch( err => {
    console.log('BULK update error');
    console.log(JSON.stringify(err, null, 2));
  });
Run Code Online (Sandbox Code Playgroud)

这里的两个关键问题是不完整的API文档问题(在撰写本文时,至少):

  • 'upsert': true 在每个文件中.Mongoose API()中没有记录这一点,它通常是指node-mongodb-native驱动程序.在这个驱动程序中查看updateOne,您可以考虑添加'options':{'upsert': true},但是,没有...这不会做.我也尝试将两种情况都添加到bulkWrite(,[options],)参数中,但也没有效果.
  • GasStation.collection.bulkWrite().虽然Mongoose bulkWrite()方法声称它应该被调用Model.bulkWrite()(在这种情况下GasStation.bulkWrite()),但它会触发MongoError: Unknown modifier: $__.所以,Model.collection.bulkWrite()必须使用.

另外,请注意:

  • 您不需要$setupdateOne.update字段中使用mongo运算符,因为mongoose在upsert的情况下处理它(请参阅示例中的bulkWrite()注释).
  • 请注意,我在模式中的唯一索引(upsert正常工作所需)定义为:

gasStationsSchema.index({ country: 1, localId: 1 }, { unique: true });

希望能帮助到你.

==>编辑:(猫鼬5?)

正如@JustinSmith所注意到的那样,$set由Mongoose添加的运营商似乎不再起作用了.也许是因为Mongoose 5?

在任何情况下,$set明确使用应该做:

'update': { '$set': gasStation },
Run Code Online (Sandbox Code Playgroud)


Mir*_*ili 14

谢谢@maganap。我使用了他/她的回答并达到了以下简洁的方法:

await Model.bulkWrite(docs.map(doc => ({
    updateOne: {
        filter: {id: doc.id},
        update: doc,
        upsert: true,
    }
})))

Run Code Online (Sandbox Code Playgroud)

或者更详细:

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(console.error.bind(console, 'BULK update error:'))
Run Code Online (Sandbox Code Playgroud)


Ada*_*eis 5

我已经为 Mongoose 发布了一个小插件,它公开了一个静态upsertMany方法来执行带有 promise 接口的批量 upsert 操作。这应该提供一种非常干净的方式来使用 Mongoose 进行批量更新,同时保留模式验证等:

MyModel.upsertMany(items, ['matchField', 'other.nestedMatchField']);
Run Code Online (Sandbox Code Playgroud)

你可以在 npm 或 Github 上找到这个插件:

https://github.com/meanie/mongoose-upsert-many https://www.npmjs.com/package/@meanie/mongoose-upsert-many


cod*_*der -4

检查这个我希望这对您有帮助 链接

链接2

我想你正在寻找

Bulk.find().upsert().update()

你可以用这个

bulk = db.yourCollection.initializeUnorderedBulkOp();
for (<your for statement>) {
    bulk.find({ID: <your id>, HASH: <your hash>}).upsert().update({<your update fields>});
}
bulk.execute(<your callback>)
Run Code Online (Sandbox Code Playgroud)
  • 如果找到,它将使用 {} 更新该文档
  • 否则,它将创建一个新文档