mongoose 判断 update-upsert 是在执行插入还是更新

问题描述 投票:0回答:3

我想测试更新的“upsert”选项是否正常。所以我用相同的键将一个对象“插入”到 mongodb 两次。但是它没有显示插入的消息。我是不是漏掉了什么?

(mongodb:v2.6.3;猫鼬:3.8.15)

Member.findOneAndRemove({user_id: 1},
    function (err, doc) {
        if (!err) onsole.log(doc ? 'deleted' : 'not found');
}); // -> deleted, make sure user_id = 1 doesn't exist

Member.update({user_id: 1}, 
    {$set: {name: "name1"}}, 
    {upsert: true, new: false}, // new : false, so that I can detect original doc is null then know it's a new one.
    function (err, doc) {
    if (!err) {
        console.log(doc ? 'updated' : 'inserted')
    }
}); // -> updated ? But it shoule be inserted, right ?

Member.update({user_id: 1}, 
    {$set: {name: "name2"}}, 
    {upsert: true, new: false},
    function (err, doc) {
    if (!err) {
        console.log(doc ? 'updated' : 'inserted')
    }
}); // -> updated, yes, no problem.

谢谢您的任何提示。

============答案=============

使用 .findOneAndUpdate 而不是 .update ! 此外,确保选项为 {upsert: true, new: false}, 这样回调的第二个参数(doc)就可以是原始文档。

javascript node.js mongodb mongoose mongodb-query
3个回答
7
投票

mongoose 中的

.update()
方法接受三个回调参数,即
err
numAffected
raw
响应。使用“原始”对象来查看发生了什么:

Member.update({user_id : 1}, 
    {$set : {name:"name1"}}, 
    {upsert : true }, 
    function (err, numAffected, raw) {
    if (!err) {
        console.log(raw)
    }
});

你会看到这样的结构:

{ ok: true,
  n: 1,
  updatedExisting: false,
  upserted: [ { index: 0, _id: 5456fc7738209001a6b5e1be } ] }

因此,创建的任何新文档始终都有

n
和 'updatedExisting
keys available, where the second is false on upserts and true otherwise.
upserted
will contain the
_id` 值。

至于

n
或“numAffected”,基本上始终为 1,其中文档在遗留写入关注响应下匹配。

您可以使用批量操作表单在 MongoDB 2.6 及更高版本中查看新的 WriteResult 响应:

var bulk = Member.collection.initializeOrderedBulkOp();
bulk.find({user_id : 1}.upsert().update({$set : {name:"name1"}});
bulk.execute(err,result) {
   console.log( JSON.stringify( result, undefined, 2 ) );
}

在第一次迭代中你会得到这样的结果:

{
  "ok": 1,
  "writeErrors": [],
  "writeConcernErrors": [],
  "nInserted": 0,
  "nUpserted": 1,
  "nMatched": 0,
  "nModified": 0,
  "nRemoved": 0,
  "upserted": [
    {
      "index": 0,
      "_id": "5456fff138209001a6b5e1c0"
    }
  ]
}

第二个具有相同参数,如下所示:

{
  "ok": 1,
  "writeErrors": [],
  "writeConcernErrors": [],
  "nInserted": 0,
  "nUpserted": 0,
  "nMatched": 1,
  "nModified": 0,
  "nRemoved": 0,
  "upserted": []
}

只有在实际发生更改的情况下,文档才会被标记为“已修改”。

因此无论如何,

.update()
操作不会返回修改后的文档或原始文档。这就是
.findOneAndUpdate()
方法,一个围绕执行原子操作的基本
.findAndModify()
的猫鼬包装器。
.update()
方法通常用于批量操作,因此不返回文档内容。


7
投票

关于使用

.findOneAndUpdate
对原始问题进行 OP 编辑,您现在还可以传递
rawResult: true
参数,该参数返回文档以及您通常从运行
.update
获得的数据,即:

{ 
  updatedExisting: true,
  n: 1,
  ok: 1
}

这应该足以回答最初的问题,即知道您是插入还是更新插入。

我发现这组选项通常适合使用

.findOneAndUpdate
:

进行更新插入
  {
    upsert: true,
    new: true,
    runValidators: true,
    setDefaultsOnInsert: true,
    rawResult: true,
  }

0
投票

可以将 timestamp 添加到您的架构中,将 new 选项设置为 true 以获取更新的文档,然后比较创建和更新值。

const personSchema = new Schema({ name: String, age: Number }, {
  timestamps: true,
})

const PersonModel = model('person', personSchema)

const filter = { name: 'John' }
const update = { age: 20 }

const document = await PersonModel.findOneAndUpdate(filter, update, {
  new: true,
  upsert: true,
})

const isNew = document.createdAt.toISOString() === document.updatedAt.toISOString()
© www.soinside.com 2019 - 2024. All rights reserved.