Reputation: 9004
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
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
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
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