Mongodb upsert only update selected fields, but insert all

agnsaft picture agnsaft · Dec 28, 2012 · Viewed 16.9k times · Source

I am trying to use upsert in MongoDB to update a single field in a document if found OR insert a whole new document with lots of fields. The problem is that it appears to me that MongoDB either replaces every field or inserts a subset of fields in its upsert operation, i.e. it can not insert more fields than it actually wants to update.

What I want to do is the following:

  • I query for a single unique value
  • If a document already exists, only a timestamp value (lets call it 'lastseen') is updated to a new value
  • If a document does not exists, I will add it with a long list of different key/value pairs that should remain static for the remainder of its lifespan.

Lets illustrate:

This example would from my understanding update the 'lastseen' date if 'name' is found, but if 'name' is not found it would only insert 'name' + 'lastseen'.

db.somecollection.update({name: "some name"},{ $set: {"lastseen": "2012-12-28"}}, {upsert:true})

If I added more fields (key/value pairs) to the second argument and drop the $set, then every field would be replaced on update, but would have the desired effect on insert. Is there anything like $insert or similar to perform operations only when inserting?

So it seems to me that I can only get one of the following:

  • The correct update behavior, but would insert a document with only a subset of the desired fields if document does not exist
  • The correct insert behavior, but would then overwrite all existing fields if document already exists

Are my understanding correct? If so, is this possible to solve with a single operation?

Answer

cerberos picture cerberos · Apr 11, 2013

MongoDB 2.4 has $setOnInsert

db.somecollection.update(
    {name: "some name"},
    {
        $set: {
            "lastseen": "2012-12-28"
        },
        $setOnInsert: {
            "firstseen": <TIMESTAMP>  # set on insert, not on update
        }
    },
    {upsert:true}
)