JavaScript is a very important language in the MongoDB ecosystem. Not only is the shell JavaScript, but the server features a built-in JS interpreter as well. The embedded interpreter is used in all sorts of different ways, including for Map/Reduce, db.eval, and $where clauses. One nice feature supported by MongoDB is the ability to store JS functions on the server.

Stored JS in MongoDB is saved in the special system.js collection. The documents there should be structured like:

{_id: "sum",
 value: function (x, y) { return x + y; }}

_id is the name of the function, and value is the JS code defining the function. Here’s an example of saving such a function and then using it in an eval. This can be run using the MongoDB shell:

>{_id: "sum",
...                  value: function (x, y) { return x + y; }});
> db.eval("return sum(2, 3);");

Once sum is defined we can use it from all of MongoDB’s JS contexts, including Map/Reduce and $where. In this example we use sum within a $where clause to get all documents where the sum of x and y is 6:

>{x: 4, y: 2});
>{x: 4, y: 3});
>{x: 3, y: 3});
> db.test.find({$where: "sum(this.x, this.y) == 6"});
{ "_id" : ObjectId("4bba376231d25858659843f7"), "x" : 4, "y" : 2 }
{ "_id" : ObjectId("4bba376e31d25858659843f9"), "x" : 3, "y" : 3 }

Since system.js is a collection, we can perform normal MongoDB operations on it in order to administer our stored JS functions. We might want to list the stored functions:

> db.system.js.distinct("_id");
[ "sum" ]

or remove a function we previously stored:

> db.system.js.remove({_id: "sum"});
> db.eval("return sum(2, 3);");
Mon Apr  5 15:14:45 JS Error: uncaught exception: {
    "errno" : -3,
    "errmsg" : "[...] sum is not defined [...]",
    "ok" : 0

Stored JS and PyMongo

In version 1.5 PyMongo added some helpers to make working with stored JS incredibly easy. The API is accessed through the Database.system_js property, which returns an instance of the SystemJS helper class. The SystemJS class allows us to add, call, and remove stored JS functions using a nice Pythonic interface:

>>> db.system_js.sum = "function (x, y) { return x + y; }"
>>> db.system_js.sum(3, 2)
>>> del db.system_js.sum
>>> db.system_js.sum(3, 2)
Traceback (most recent call last):
pymongo.errors.OperationFailure: [...] sum is not defined [...]

The implementation of the SystemJS class is actually pretty trivial. The trickiest bit is getting the argument passing right between the Python lambda returned by __getattr__ and the invocation of the stored JS function within the eval.