javascriptcouchdbcloudantcouchapp

couchdb/cloudant update handler not working as expected


I am developing an app that uses Cloudant as the database. I have a situation where I make two database calls at almost the time to the same document, but, because of it, I get a conflict error. So I tried to create an update handler function in a cloudant database that looks like this:

{
  "_id": "_design/_updateHandler",
  "updates": {
    "in-place": "function(doc, req) {
        var field = req.body.field,
        var value= req.body.value,
        var doc[field] = value;
        return [doc, toJSON(doc)];
    }"
  }
}

And it adds a field in the document, but it doesn't update an existing field of it. Is it the expected behavior? It doesn't look like so in the cloudant documentation for update handlers nor in the couchdb documentation.

If not, how can I fix this problem so it does update an existing field? Also, as answered in this question, an update handler function would still get an update conflict, but how can I avoid it?

Thanks in advance :)


Solution

  • Here is a working example of a CouchDB and Cloudant compatible Update Function to do what you're after--update a single field.

    function(doc, req) {
      if (!doc) doc = {_id: req.uuid};
      var body = JSON.parse(req.body);
      var field = body.field;
      var value = body.value;
      doc[field] = value;
      return [doc, JSON.stringify(doc)];
    }
    

    The body of the POST request is considered to be valid JSON, so you may want to do some header checks for application/json.

    Additionally, if you only plan to do field = value stuff like you have here, then application/x-www-form-url-encoded would be better as it will lower your payload size and parsing time--CouchDB and Cloudant will auto-parse that media type into the req.form object.

    Lastly, it's best to never prefix anything you give to CouchDB or Cloudant with an underscore as its considered a reserved character at the beginning of top level fields and _id values. The _design/_updateHandler name is (subsequently) very confusing...

    Here's the JSON to copy/paste into your database to get a working update function to do what you wanted:

    {
        "_id": "_design/overwrite",
        "updates": {
            "in-place": "function(doc, req) {\n  if (!doc) doc = {_id: req.uuid};\n  var body = JSON.parse(req.body);\n  var field = body.field;\n  var value = body.value;\n  doc[field] = value;\n  doc.body = body;\n  return [doc, JSON.stringify(doc)];\n}"
        }
    }
    

    To update an existing doc you'd make an HTTP request like this one (where bigbluehat is the previously stored document's _id:

    POST /db/_design/overwrite/_update/in-place/bigbluehat
    Content-Type: application/json
    
    {"field": "name", "value": "BigBlueHat"}
    

    Or, if you don't include the document _id in the request URL, you'll get a new document which uses the req.uuid value to store a new document.

    Hope that helps!