最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - mongoose to determine update-upsert is doing insert or update - Stack Overflow

programmeradmin5浏览0评论

I want to test if "upsert" option for updating is woking fine. So I "upsert" an object into mongodb twice with same key. However it didn't show inserted message. Was I missing something ?

(mongodb : v2.6.3; mongoose : 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.

Thank you for any hint.

============ answer =============

Use .findOneAndUpdate instead of .update ! Moreover, make sure option is {upsert: true, new: false}, so that the callback's 2nd parameter(doc) could be original document in case.

I want to test if "upsert" option for updating is woking fine. So I "upsert" an object into mongodb twice with same key. However it didn't show inserted message. Was I missing something ?

(mongodb : v2.6.3; mongoose : 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.

Thank you for any hint.

============ answer =============

Use .findOneAndUpdate instead of .update ! Moreover, make sure option is {upsert: true, new: false}, so that the callback's 2nd parameter(doc) could be original document in case.

Share Improve this question edited May 19, 2020 at 3:49 nwpie asked Nov 3, 2014 at 5:00 nwpienwpie 7351 gold badge11 silver badges26 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 7

The .update() method in mongoose takes three arguments to the callback, being err, the numAffected, and a raw response. Use the "raw" object to see what happened:

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

You'll see a structure like this:

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

So there is always the n and 'updatedExistingkeys available, where the second is false on upserts and true otherwise.upsertedwill contain the_id` values of any new documents created.

As for n or the "numAffected", this is basically always 1 where a document was matched under the legacy write concern responses.

You can see the new WriteResult response in MongoDB 2.6 and above using the Bulk Operations form:

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

Which on a first iteration you get something like this:

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

And a second with the same parameters like this:

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

And the document would only be marked as "modified" where something was actually changed.

So at any rate, .update() operations do not return the modified document or the original document. That is the .findOneAndUpdate() method, a mongoose wrapper around the basic .findAndModify() which performs an atomic operation. The .update() methods are typically meant for bulk operations and as such do not return document content.

Regarding the OPs edit of the original question to use .findOneAndUpdate, you can also now pass the rawResult: true parameter, which return the document along with the data you would normally get from running .update, i.e.:

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

Which should be sufficient to answer the original question of knowing whether you inserted or upserted.

I find this set of options to be generally desirable for upserts using .findOneAndUpdate:

  {
    upsert: true,
    new: true,
    runValidators: true,
    setDefaultsOnInsert: true,
    rawResult: true,
  }

May add timestamp to your schema, set the new option to true to get the updated or created document back and then pare createdAt & updatedAt values.

const memberSchema = new Schema({ user_id: String, age: Number }, {
  timestamps: true,
})

const Member = model('member', memberSchema)

const filter = { user_id: 'abcdef' }
const update = { age: 20 }

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

const isInserted = document.createdAt.toISOString() === document.updatedAt.toISOString()
发布评论

评论列表(0)

  1. 暂无评论