Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mongodb: upserting: only set value if document is being inserted

Tags:

mongodb

Considering a simple mongo document structure:

{ _id, firstTime, lastTime }

The client needs to insert a document with a known ID, or update an existing document. The 'lastTime' should always be set to some latest time. For the 'firstTime', if a document is being inserted, then the 'firstTime' should be set to current time. However, if the document is already created, then 'firstTime' remain unchanged. I would like to do it purely with upserts (to avoid look ups).

I've crawled the http://www.mongodb.org/display/DOCS/Updating, but I just don't see how that particular operation can be done.

I don't believe this is something unreasonable, there are $push and $addToSet operations that effectively do that on array fields, just nothing that would do the same on simple fields. It's like there should be something like $setIf operation.

like image 421
Pawel Veselov Avatar asked May 20 '12 07:05

Pawel Veselov


3 Answers

I ran into the exact same problem and there was no simple solution for <2.4 however since 2.4 the $setOnInsert operator let's you do exactly that.

db.collection.update( <query>,
                      { $setOnInsert: { "firstTime": <TIMESTAMP> } },
                      { upsert: true }
                    )

See the 2.4 release notes of setOnInsert for more info.

like image 147
domguinard Avatar answered Sep 28 '22 16:09

domguinard


I ran into a very similar problem when attempting to upsert documents based on existing content--maybe this solution will work for you also:

Try removing the _id attribute from your record and only use it in the query portion of your update (you'll have to translate from pymongo speak...)

myid = doc.get('_id')
del doc['_id']
mycollection.update({'_id':myid}, {'$set':doc}, upsert=True)
like image 28
jeffh Avatar answered Sep 28 '22 16:09

jeffh


If you will trigger the following code 2 subsequent times, it will first set both firstVisit and lastVisit on document insert (and will return upsertedId in the response) and on the second it will only update lastVisit (and will return modifiedCount: 1).

Tested with Mongo 4.0.5 though I believe should be working with older versions.

db.collection.updateOne(
  {_id: 1}, 
  { 
    $set: { 
      lastVisit: Date.now() 
    }, 
    $setOnInsert: {
      firstVisit: Date.now()
    }
  }, 
  { upsert: true }
);
like image 1
Andrei Petrik Avatar answered Sep 28 '22 18:09

Andrei Petrik