CouchDB Document Update Handlers (in-place updates)
Solution 1:
The example function in-place
is not the same as "in-place" updates in other
databases. CouchDB still uses an append-only architecture; document update
handlers still create a new doc revision, etc.
Still, update handlers are quite convenient and worth learning.
Suppose you have a document with an accumulator. You
want to accumulate an integer in a document with just one HTTP query, specifying
the increment amount using an amount
parameter. Consider the following commands:
curl -X PUT http://localhost:5984/db
# Returns {"ok":true}
curl -H "Content-Type:application/json" -X POST http://localhost:5984/db/_bulk_docs -d @-
{"docs":
[
{"_id": "my_doc", "number": 23},
{"_id": "_design/app",
"updates": {
"accumulate": "function (doc, req) {
var inc_amount = parseInt(req.query.amount);
doc.number = doc.number + inc_amount;
return [doc, \"I incremented \" +
doc._id + \" by \" +
inc_amount];
}"
}
}
]
}
# Returns [{"id":"my_doc","rev":"1-8c9c19a45a7e2dac735005bbe301eb15"},
# {"id":"_design/app","rev":"1-83ec85978d1ed32ee741ce767c83d06e"}]
(Remember to press end-of-file, ^D, after the JSON object in the POST.)
Next confirm the document for accumulation (my_doc
) exists:
curl http://localhost:5984/db/my_doc
# Returns {"_id":"my_doc","_rev":"1-8c9c19a45a7e2dac735005bbe301eb15",
# "number":23}
Now you can call the accumulate
update handler with an amount
parameter to
update the field.
curl -X PUT \
http://localhost:5984/db/_design/app/_update/accumulate/my_doc?amount=15
# Returns: I incremented my_doc by 15
curl http://localhost:5984/db/my_doc
# Returns {"_id":"my_doc","_rev":"2-<whatever>",
# "number":38}
Notice that the new number
value is 38, the value of 23 + 15.
Solution 2:
Here is something I found very useful when playing with the above example. Once you've installed an update handler, you can use it to update the update-handler itself. For instance, if your update handler is in update.json, you could do the following:
curl --dump-header - -H "Content-Type:application/json" -X POST http://localhost:5984/db/_design/app/_update/modifyinplace/_design/app -d @update.json
where update.json contains:
{"_id": "_design/app",
"updates": {
"modifyinplace": "function (doc, req) { var fields = JSON.parse(req.body); for (var i in fields) { doc[i] = fields[i] } var resp = eval(uneval(doc)); delete resp._revisions; return [doc, toJSON(resp)]; }"
}
}
A few things are noteworthy. The statement var resp = eval(uneval(doc))
clones doc
. There is more information on cloning here. Since the _revisions
field can get large, deleting it in the response makes sense for my use case. The use of toJSON(resp)
sends back the response as a string, however, the value of _rev
in a successful response will be wrong. To get the correct revision of a successful update, look at X-Couch-Update-NewRev
in the response header. The update could very well not succeed and result in a conflict, as addressed here