1

I know there are a couple of similar questions out there (like this one for example: CouchDB - trigger code when creating or updating document) which refer to "update handlers" in CouchDB but I have not been able to see how they solve the issue;

  • Each time a new document is created in my CouchDB database I want to add a timestamp
  • I've created an update handler in _design/updatehandlers and verified that it gets called:

    "timestampadd" : "function(doc,req) {
    if (!doc) { 
        if ( req.id) { 
            return [{_id : req.id, timestamp : new Date().getTime()}];
        }
        return [null,'added empty'];
    }
    doc.timestamp = new Date().getTime(); 
    return [doc,'added at ' + doc.timestamp];
    }"
    

My question is this; How do can I use this function when creating a new document in the database, i.e. one which I would normally create by doing a POST to

    http://mycouchdb:5984/test_db

where I want couchdb to create an _id for me (see for example: http://docs.cloudant.com/tutorials/crud/index.html#create)?

If I follow the documentation to do it (in Python in this example) I would issue a Post like so;

   requests.post("http://mycouchdb:5984/test_db/_design/updatehandlers/_update/timestampadd",headers={'Content-type':'application/json'},data='{"foo":"bar"})

but here the update function timestampadd takes the "doc==null && req.id==null path, obviously, since the document has not yet been created and I didn't specify an ID. This leads me to believe that the update function can only be invoked on an existing document, despite what the documentation seems to imply, or am I wrong?

I can invoke the update on an existing document and add the timestamp without any trouble, that works well, but the issue here was to add a timestamp as the document is created.

Is that even possible..?

Community
  • 1
  • 1
SonarJetLens
  • 386
  • 1
  • 9

2 Answers2

4

Yes you can do that with an update handler. You're on the right track. Here are the 3 cases:

Create a doc without specifying an id

The path taken is !doc && !req.id. That's your example. Instead of returning null, you should build a new document and return it. You can use req.uuid to populate the _id. For instance:

return [{_id : req.uuid, timestamp : new Date().getTime()}, 'created new doc'];

Create a doc with a given id

The path taken is !doc && req.id. You got this one right.

Update an existing doc

The path taken is doc. You got this one right.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Simon
  • 31,675
  • 9
  • 80
  • 92
1

Thanks to Simon for pointing me in the right direction. This was the script I ended up using;

function(doc,req) { 
   if (!doc) { 
       var new_doc = {}; 
       if ( req.id) { 
           new_doc._id = req.id; 
       } else { 
            new_doc._id = req.uuid; 
       } 
       new_doc.timestamp = new Date().getTime(); 
       new_doc.body = eval('(' + req.body + ')'); 
       return [new_doc,'added new']; 
   } 
   doc.timestamp = new Date().getTime(); return [doc,'updated'];
 }

Notice that I had to add an "eval" with the body passed in through the request to add the contents of the new document to the ones provided by the caller.

SonarJetLens
  • 386
  • 1
  • 9
  • 1
    This is almost correct but `JSON.parse` should be preferred over `eval`. Eval is inefficient and introduces security issues. Moreover when `doc` is not `null`, that is during a `PUT` this code will ignore any change to the document, but only update the timestamp. – z1naOK9nu8iY5A Jul 07 '16 at 15:59