mongodb 尝试用 Mongoose 做一个大的颠覆,什么是最干净的方法来做这个?

k7fdbhmy  于 2023-01-08  发布在  Go
关注(0)|答案(8)|浏览(108)

我有一个保存文档的集合,其中包含三个字段: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)
})

'
我的用户模型如下所示:

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;
68bkxrlz

68bkxrlz1#

  • ( Mongoose @4.9.1,蒙哥b@3.4.2)*
    • TL; DR**
await GasStation.collection.bulkWrite([ // <<==== use the model name
  {
    'updateOne': {
      'filter': { 'id': '<some id>' },
      'update': { '$set': { /* properties to update */ } },
      'upsert': true,  // <<==== upsert in every document
    }
  },
  /* other operations here... */
]);
    • 说来话长**

在与Mongoose API糟糕的文档斗争之后,我解决了bulkWrite()方法中的批量upsert调整updateOne:{}操作。
一些未记录的事情需要考虑:

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

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

  • 'upsert': true在每个文档中。Mongoose API()中没有说明这一点,它通常引用 * node-mongodb-native * 驱动程序。查看此驱动程序中的updateOne,您可能会想到添加'options':{'upsert': true},但是,不......这行不通。我还尝试将这两种情况添加到bulkWrite(,[options],)参数中,也没有效果。
  • 尽管Mongoose bulkWrite()方法声称它应该被称为Model.bulkWrite()(在本例中为GasStation.bulkWrite()),但这将触发MongoError: Unknown modifier: $__,因此必须使用Model.collection.bulkWrite()

此外,请注意:

  • 您不需要在updateOne.update字段中使用$set mongo操作符,因为mongoose会在upsert的情况下处理它(参见示例中的bulkWrite()注解)。
  • 注意,模式中的唯一索引(upsert正常工作所需的)定义为:

gasStationsSchema.index({ country: 1, localId: 1 }, { unique: true });
希望有帮助。

  • ==〉编辑:( Mongoose 5?)*

@JustinSmith注意到,Mongoose添加的$set操作符似乎已经不起作用了,可能是因为Mongoose 5的缘故吧?
在任何情况下,显式使用$set都应该:

'update': { '$set': gasStation },
ep6jt1vc

ep6jt1vc2#

感谢@maganap.我使用his/her answer,并达到以下简洁的方法:

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(console.log.bind(console, 'BULK update OK:', bulkWriteOpResult))
        .catch(console.error.bind(console, 'BULK update error:'))
flvtvl50

flvtvl503#

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

MyModel.upsertMany(items, ['matchField', 'other.nestedMatchField']);

你可以在npm或Github上找到这个插件:
https://github.com/meanie/mongoose-upsert-manyhttps://www.npmjs.com/package/@meanie/mongoose-upsert-many

nfeuvbwi

nfeuvbwi4#

我尝试了上面的@magnap解决方案,发现它覆盖了我只想更新的现有文档,而不是更新我在updates.updateOne中设置的字段,它选择了文档并将其所有字段替换为在.update中指定的字段。
我最终不得不在update方法中使用$set来解决这个问题。下面是我的控制器的最终外观:

const { ObjectId } = require('mongodb');

exports.bulkUpsert = (req, res, next) => {
     const { updates } = req.body;
     const bulkOps = updates.map(update => ({
         updateOne: {
             filter: { _id: ObjectId(update.id) },
             // Where field is the field you want to update
             update: { $set: { field: update.field } },
             upsert: true
          }
      }));
    // where Model is the name of your model
    return Model.collection
        .bulkWrite(bulkOps)
        .then(results => res.json(results))
        .catch(err => next(err));
};

这适用于Mongoose 5.1.2。

of1yzvn4

of1yzvn45#

您可以使用array.map来代替for

const result = await Model.bulkWrite(
    documents.map(document => {

        document = {
          ...document, 
          ...{
            last_update: Date.now(),
            foo: 'bar'
          }
        }

        return {
          updateOne: {
            filter: { document_id: document.document_id }, //filter for each item
            update: {
              $set: document,//update whole document
              $inc: { version: 1 }//increase version + 1
            },
            upsert: true //upsert document
          }
        }

      }
    ));
i5desfxk

i5desfxk6#

希望我在这里的回答能帮到你。它处理批量upsert为电子商务域异步

7qhs6swi

7qhs6swi7#

在上找到官方解决方案:https://docs.mongodb.com/manual/reference/method/Bulk.find.upsert/
Mongoose也支持相同的链。

Bulk.find(<query>).upsert().update(<update>);
Bulk.find(<query>).upsert().updateOne(<update>);
Bulk.find(<query>).upsert().replaceOne(<replacement>);

测试其工作原理:

BulkWriteResult {
  result:
   { ok: 1,
     writeErrors: [],
     writeConcernErrors: [],
     insertedIds: [],
     nInserted: 0,
     nUpserted: 1,
     nMatched: 4186,
     nModified: 0,
     nRemoved: 0,
     upserted: [ [Object] ] } }
9q78igpj

9q78igpj8#

  • 检查这个,我希望这对你有帮助 * link

link2

我想你是在找

查找()更新()
你可以用这个

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>)
  • 如果找到一个,它将使用{}更新该文档
  • 否则,它将创建一个新文档

相关问题