Skip to content

Instantly share code, notes, and snippets.

@jmakeig
Last active February 25, 2017 17:58
Show Gist options
  • Save jmakeig/1faceb1ce8d7d1522c383729cdf2d8d7 to your computer and use it in GitHub Desktop.
Save jmakeig/1faceb1ce8d7d1522c383729cdf2d8d7 to your computer and use it in GitHub Desktop.
Decorator function for updates in MarkLogic that wraps a document update with a lock on the URI.
'use strict';
declareUpdate();
/**
* An example function that take a Document node and some paramaters and returns
* an updated copy, in Object form. Specifically, it adds a property with both the
* name and value of the `letter` parameter. By design, this function doesn’t actually
* change the input Document node. The point here is to decouple the mutation
* of the document from the update in the database.
*
* @param {Node} doc - The document to update
* @param {string} letter - The key and value of the JSON property to add
* @return {Object} - The updated document as a JavaScript Object
*/
function addStuff(doc, letter) {
var obj = doc.toObject();
obj[letter] = letter;
return obj;
}
// Decorate `addStuff` to handle the lock and node update.
var addLetter = lockForUpdate(addStuff);
for(var uri of cts.uris()) {
addLetter(uri, 'r');
}
'use strict';
declareUpdate();
/**
* Creates a write lock on a URI and updates the document at the URI
* with the result of calling the `transformer` function with the `Document`
* at the URI.
*
* @example
* const updateWithTransform = (uri, letter) => lockAndUpdate(uri, (doc) => doTransform(doc, letter));
*
* @param {string} uri - the URI of the document to update
* @param {function} transformer - a pure function that transforms a document
* @return {object|Node} - the unmodified return value of calling `transformer(doc)`
* for downstream processing
* @throws {ReferenceError} - missing uri or the document at the URI doesn’t exist
* @throws {TypeError} - non-function or missing transformer
*/
function lockAndUpdate(uri, transformer) {
if(null === uri || undefined === uri) { throw new ReferenceError(`uri must be string`); }
uri = String(uri);
if('function' !== typeof transformer) { throw new TypeError(`transformer must be a function`); }
xdmp.lockForUpdate(uri);
const doc = cts.doc(uri);
if(doc) {
const result = transformer(doc);
xdmp.nodeReplace(doc, result);
return result;
} else {
throw new ReferenceError(`No document exists at ${uri}`);
}
}
// Pure function that takes a Document node and some parameters.
// Returns the new state of the doc.
function doTransform(doc, letter) {
return Object.assign(doc.toObject(), { [letter]: letter });
}
const updateWithTransform = (uri, letter) => lockAndUpdate(uri, (doc) => doTransform(doc, letter));
for(const uri of cts.uris()) {
updateWithTransform(uri, 'h');
}
/**
* Decorates a function that updates documents in order to 1) lock the URI of the document
* and 2) acutally perform the database update. The `updater` implementation is passed a
* Document node and any other runtime arguments. It need only conern itself with
* returning a new version of that document. It need not (read: should not) worry
* about the mechanics up locking or updating.
*
* @param {function} updater - The function that actually does the updates.
* The first param is assumed to be the URI of the
* document to be updated. When called, `updater` is
* passed the the actual Document node and any other
* parameters originall passed in through the decorator.
* @return {function} - A decorator function with the signature `(document, ...other)`
*/
function lockAndUpdate(updater) {
if('function' !== typeof updater) { throw new TypeError(`${String(f)} is not a function`); }
return function _wrappedLockForUpdate() {
if(arguments.length < 1) { throw new ReferenceError(`The first parameter must be a URI as a string`); }
var args = Array.prototype.slice.call(arguments);
var uri = args.shift();
//var doc = args.shift();
if(uri) {
xdmp.lockForUpdate(uri);
var doc = cts.doc(uri);
//if(!(doc instanceof Document)) { throw new TypeError(`Must be a Document node`); }
//xdmp.lockForUpdate(xdmp.nodeUri(doc));
if(doc) {
var result = updater.apply(null, [doc].concat(args));
xdmp.nodeReplace(doc, result);
return result;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment