Tzury Bar Yochay
Tzury Bar Yochay

Reputation: 9004

mongodb update with upsert:true does not act as in insert

I am trying to use update with upsert whiwle providing my own _id as key.

As it turns out, it works only when I use insert, if upsert:true is provided with the update, the new inserted doc gets Mongo's auto-generated id.

See bellow:

PRIMARY> db.internal.update({_id: "my_id"},{ "value": "xyz"}, {upsert:true})
PRIMARY> db.internal.find()
{ "_id" : ObjectId("50c6cbb21d8b512bc0fe9576"), "value" : "xyz" }
PRIMARY> db.internal.insert({_id: "my_id2", "value": "xyz"})
PRIMARY> db.internal.find()
{ "_id" : ObjectId("50c6cbb21d8b512bc0fe9576"), "value" : "xyz" }
{ "_id" : "my_id2", "value" : "xyz" }

Is this a feature or a bug?

According to what I see in Mongo's docs, this shall work

Upvotes: 5

Views: 9494

Answers (3)

Tzury Bar Yochay
Tzury Bar Yochay

Reputation: 9004

Just found out that if I provide the _id again at the data part, in addition to the condition part, then it works.

e.g.

PRIMARY> db.internal.update({_id: "my_id3"},{ _id: "my_id3", "value": "xyz"}, {upsert:true})
PRIMARY> db.internal.find()
{ "_id" : ObjectId("50c6cbb21d8b512bc0fe9576"), "value" : "xyz" }
{ "_id" : "my_id2", "value" : "xyz" }
{ "_id" : "my_id3", "value" : "xyz" }

However, this is not appearing to be the api intended - as you can see in the docs example where { _id:7 } is being provided along with { upsert:true }

db.bios.update(
   {
     _id: 7,
     name: { first: 'Ken', last: 'Thompson' }
   },
   {
     $set: {
              birth: new Date('Feb 04, 1943'),
              contribs: [ 'UNIX', 'C', 'B', 'UTF-8' ],
              awards: [
                        {
                          award: 'Turing Award',
                          year: 1983,
                          by: 'ACM'
                        },
                        {
                          award: 'IEEE Richard W. Hamming Medal',
                          year: 1990,
                          by: 'IEEE'
                        },
                        {
                          award: 'National Medal of Technology',
                          year: 1998,
                          by: 'United States'
                        },
                        {
                          award: 'Tsutomu Kanai Award',
                          year: 1999,
                          by: 'IEEE'
                        },
                        {
                          award: 'Japan Prize',
                          year: 2011,
                          by: 'The Japan Prize Foundation'
                        }
             ]
           }
   },
   { upsert: true }
)

Upvotes: 0

shakefu
shakefu

Reputation: 91

It should be noted that without a $set/$pull/etc., modifier, you're just replacing the document with the second param.

> db.test.update({_id: 'foo'}, {_id: 'foo', value: 'xyz'}, upsert=true)
> db.test.find()
{ "_id" : "foo", "value" : "xyz" }
> db.test.update({_id: 'foo'}, {_id: 'foo', value: 'hello'}, upsert=true)
> db.test.find()
{ "_id" : "foo", "value" : "hello" }

Upvotes: 0

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230286

Yes, that's a little known gotcha. The trick is to use a $set modifier with the upsert. It will then combine your update and query parts to form the upserted document. Look:

db.internal.update({_id: "my_id"},{"$set": {"value": "xyz"}}, {upsert:true})
db.internal.find()
// { "_id" : "my_id", "value" : "xyz" }

Upvotes: 11

Related Questions