Differences between revisions 15 and 16
Revision 15 as of 2010-12-27 06:58:58
Size: 5959
Editor: c-71-63-158-40
Comment: adding a lot more info based on my documentation at http://couchapp.org/page/update-functions
Revision 16 as of 2010-12-28 16:51:24
Size: 5601
Editor: adsl-99-136-195-13
Comment: Minor wordsmithing and linking to a relevant JIRA issue
Deletions are marked like this. Additions are marked like this.
Line 7: Line 7:
CouchDB (0.10 and up) has the ability to allow server-side updating of a document without the usual GET-modify-POST cycle, or server-side creation of a new document based on parameters sent by the user. This feature allows a range of use cases such as providing a server-side last modified timestamp, updating individual fields in a document without first getting the latest revision, etc. This also allows for the CouchDB server to provide a GET-modify-POST cycle that does not depend on client-side JS to create or edit documents. Update handler are functions clients can request to invoke server-side logic that will create or update a document. This feature allows a range of use cases such as providing a server-side last modified timestamp, updating individual fields in a document without first getting the latest revision, etc.
Line 9: Line 9:
The update handler can be passed a document id (via the URI( to show that it will be editing that document, and the server will provide the function with the most recent version of that document. Anything else passed to the handler happens through the POST/PUT body or the query string, and must be handled in the handler. When the request to an update handler includes a document ID in the URL, the server will provide the function with the most recent version of that document. You can provide any other values needed by the update handler function via the POST/PUT entity body or query string parameters of the request.
Line 11: Line 11:
== Implementation ==
This functionality is implemented via document update handlers defined in a design doc. Specifically, in a design doc one defines an "updates" attribute that contains any number of document update handlers. The follow handlers should be self-explanatory as to what they accomplish.
This feature was first implemented in CouchDB version 0.10.

== Creating an Update Handler ==
You can specify any number of update handler functions in a design document, under the "updates" attribute.

For example:
Line 68: Line 72:
NOTE: '''The functions should be quoted'''. NOTE: '''The functions should be double-quoted JSON strings'''.
Line 77: Line 81:
    {"info":
     {
      
"db_name":"loot",
      
/* and many more */
      "committed_update_seq":27
     },
     "id":null,
     "uuid":
"7f8a0e3833bcc7161cfab80275221dc1",
     "method":"POST",
     "path":["loot","_design","haul","_update","new"],
     "query":{},
     
"headers":{"Accept":"application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" /* and many more */},
     "body":"name=Jeff",
     "peer":"127.0.0.1",
     "form":{"name":"Jeff"},
     "cookie":{},
     
"userCtx":{"db":"loot","name":null,"roles":[]}
    }
{
  "info": {
    
"db_name": "loot",
    
/* and many more */
    "committed_update_seq": 27
  },
  "id": null,
  "uuid":
"7f8a0e3833bcc7161cfab80275221dc1",
  "method": "POST",
  "path": ["loot", "_design", "haul", "_update", "new"],
  "query": {},
  
"headers": {"Accept": "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" /* and many more */},
  "body": "name=Jeff",
  "peer": "127.0.0.1",
  "form": {"name": "Jeff"},
  "cookie": {},
  
"userCtx": {"db": "loot", "name": null, "roles": []}
}
Line 97: Line 101:
Since no ID was passed, the request doesn't have an ID. However, the CouchDB server helpfully throws in a UUID so you can create a new document with a unique ID and be sure it won't conflict with any document in the database already. Since no ID was passed, the request doesn't have an ID. However, the CouchDB server helpfully provides a UUID so you can create a new document with a unique ID and be sure it won't conflict with any document in the database already.
Line 105: Line 109:
    var resp = {
      "headers" : {
     "Content-Type" : "application/xml"
      },
     "body" : doc.xml
    };
var resp = {
  "headers" : {
    "Content-Type" : "application/xml"
  },
  "body" : doc.xml
};
Line 117: Line 121:
Though you can set the headers, right now the status code for an update function response is hardcoded to be 200/201 unless an error occurs.  This is a bug. Though you can set the headers, right now the status code for an update function response is hardcoded to be 200/201 unless an error occurs. See [[https://issues.apache.org/jira/browse/COUCHDB-648|this issue]] in JIRA.
Line 136: Line 140:

== TBD ==
 * Maybe we should support PATCH?

Document Update Handlers

Basics

Update handler are functions clients can request to invoke server-side logic that will create or update a document. This feature allows a range of use cases such as providing a server-side last modified timestamp, updating individual fields in a document without first getting the latest revision, etc.

When the request to an update handler includes a document ID in the URL, the server will provide the function with the most recent version of that document. You can provide any other values needed by the update handler function via the POST/PUT entity body or query string parameters of the request.

This feature was first implemented in CouchDB version 0.10.

Creating an Update Handler

You can specify any number of update handler functions in a design document, under the "updates" attribute.

For example:

   1 {
   2   updates: {
   3 
   4     "hello" : function(doc, req) {
   5       if (!doc) {
   6         if (req.id) {
   7           return [{
   8             _id : req.id
   9           }, "New World"]
  10         }
  11         return [null, "Empty World"];
  12       }
  13       doc.world = "hello";
  14       doc.edited_by = req.userCtx;
  15       return [doc, "hello doc"];
  16     },
  17 
  18     "in-place" : function(doc, req) {
  19       var field = req.form.field;
  20       var value = req.form.value;
  21       var message = "set "+field+" to "+value;
  22       doc[field] = value;
  23       return [doc, message];
  24     },
  25 
  26     "bump-counter" : function(doc, req) {
  27       if (!doc.counter) doc.counter = 0;
  28       doc.counter += 1;
  29       var message = "<h1>bumped it!</h1>";
  30       return [doc, message];
  31     },
  32 
  33     "error" : function(doc, req) {
  34       superFail.badCrash;
  35     },
  36 
  37     "xml" : function(doc, req) {
  38       var xml = new XML('<xml></xml>');
  39       xml.title = doc.title;
  40       var posted_xml = new XML(req.body);
  41       doc.via_xml = posted_xml.foo.toString();
  42       var resp =  {
  43         "headers" : {
  44           "Content-Type" : "application/xml"
  45         },
  46         "body" : xml
  47       };
  48 
  49        return [doc, resp];
  50      }
  51   }
  52 }

NOTE: The functions should be double-quoted JSON strings.

The handler function takes the most recent version of the document from the database and the http request environment as parameters. It returns a two-element array: the first element is the (updated or new) document, which is committed to the database. If the first element is null no document will be committed to the database. If you are updating an existing, it should already have an _id set, and if you are creating a new document, make sure to set its _id to something, either generated based on the input or the req.uuid provided. The second element is the response that will be sent back to the caller.

Request

The request parameter will look something like this for a update function designed to create a new document:

Syntax highlighting not supported for 'javacript', see HelpOnParsers.
   1 {
   2   "info": {
   3     "db_name": "loot",
   4     /* and many more */
   5     "committed_update_seq": 27
   6   },
   7   "id": null,
   8   "uuid": "7f8a0e3833bcc7161cfab80275221dc1",
   9   "method": "POST",
  10   "path": ["loot", "_design", "haul", "_update", "new"],
  11   "query": {},
  12   "headers": {"Accept": "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" /* and many more */},
  13   "body": "name=Jeff",
  14   "peer": "127.0.0.1",
  15   "form": {"name": "Jeff"},
  16   "cookie": {},
  17   "userCtx": {"db": "loot", "name": null, "roles": []}
  18 }

Since no ID was passed, the request doesn't have an ID. However, the CouchDB server helpfully provides a UUID so you can create a new document with a unique ID and be sure it won't conflict with any document in the database already.

The server also parses the POST body into a Javascript object called form and does the same with the query string, in query.

Response

The second member of the return array is the HTTP response. This can be a javascript object with headers and a body:

   1 var resp =  {
   2   "headers" : {
   3     "Content-Type" : "application/xml"
   4   },
   5   "body" : doc.xml
   6 };

or just a plain string:

    <p>Update function complete!</p>

Though you can set the headers, right now the status code for an update function response is hardcoded to be 200/201 unless an error occurs. See this issue in JIRA.

Usage

To invoke a handler, use one of:

  • a PUT request against the handler function with a document id: /<database>/_design/<design>/_update/<function>/<docid>

  • a POST request agasint the handler function without a document id: /<database>/_design/<design>/_update/<function>

The document id specified in a PUT request URI is available in the update handler as id property on the request object (req.id).

For example, to invoke the in-place handler defined above, PUT to:

http://127.0.0.1:5984/<my_database>/_design/<my_designdoc>/_update/in-place/<mydocId>?field=title&value=test

This means that unlike document validators, the user's intent must be clear by calling this individual handler explicitly. In this sense, you should think about an _update handler as complementary to _show functions, not to validate_doc_update functions.

For more information, look at update_documents.js in the test suite.