
DovetailDB supports Javascript stored procedures that will execute remotely. This feature allows you to perform complex operations and calculations that would otherwise be prohibitive to perform on the client side. It can also be used to control access to data in a highly customizable manner.
Here is an example code file. You can try this out on a local instance of DovetailDB; Read the code below and save it to a file on your machine named "mycode01.js".
// A function for clients to call
function numOlderThan(api, age) {
// when called from the outside, the "api" parameter is automatically supplied
itr = api.query("people", [">", age], {});
var ctr = 0;
while (itr.hasNext()) { ctr++; itr.next(); }
return ctr;
}
function wrapApi(api, req) {
// backdoor to access the raw, wide open api
if (req.getParameter("accesskey") == "YourSecretSuperuserKey") {
return api;
}
// do not let people run arbitrary code:
if (req.getAction() == Packages.dovetaildb.Action.execute) {
return null;
}
// limit function calls to the one function we want to expose:
if (req.getAction() == Packages.dovetaildb.Action.call) {
if (req.getFunctionName() != 'numOlderThan') {
return null;
}
}
// The other actions are the basic CRUD operations; we'll allow those,
// but intercept some to add functionality:
var overrides = {
// Attach a lastModified atribute to every write:
"put": function(bag, entry) {
entry.put("lastModified", new Date().getTime());
api.put(bag, entry);
},
// Hide people with status "hidden":
// It's worth noting that interceptors apply beneath function calls,
// so the numOlderThan() function will also not count "hidden" people.
"query": function(bag, query, options) {
if (bag == "people") {
query = ["&", [query, {"status": ["!", "hidden"]}]];
}
return api.query(bag, query, options);
},
// Move all deleted things into graveyard bags
"remove": function(bag, object_id) {
entry = api.query(bag, {"id":object_id}, {}).next()
api.put(bag + "_graveyard", entry);
api.remove(bag, object_id);
}
};
// This code uses <overrides> to make a class that has the official interface:
newApi = new JavaAdapter(Packages.dovetaildb.api.WrappingApiService, overrides);
newApi.setInner(api);
return newApi;
};
// This code registers our wrapApi() function with the database
apiplugins.add(Packages.dovetaildb.api.Plugin(code={'wrapApiService':wrapApi}));Then, run a command like the following to upload the code to a database. You may specify multiple files on the command line. Running this command will replace any existing
java -jar dovetaildb.jar uploadcode -u 'http://localhost:19156' -d sandbox mycode01.js
Now you can try out the various features:
curl http://localhost:19156/sandbox/put -0 -d bag=people \
-d 'entry={"id":"Jack","age":35, "status":"normal"}'
curl http://localhost:19156/sandbox/query -0 -d bag=people -d 'query={"age":[">=",4]}'
curl http://localhost:19156/sandbox/call -0 -d function=numOlderThan -d 'args=[4]'
curl http://localhost:19156/sandbox/remove -0 -d bag=people -d 'id=Joe'
curl http://localhost:19156/sandbox/execute -0 -d 'code=api.query("people",{},{})' \
-d 'accesskey=YourSecretSuperuserKey'