January 2, 2023 · couchdb, database and javascript

couchilla is a bundler for packing design documents for CouchDB.

Design documents are a special type of database entry that you can insert to CouchDB, they contain functions such as view and update functions. These functions are executed when requested and create secondary indexes, i.e. MapReduce views.

CouchDB ships with JavaScript and Erlang support, but design functions are language-agnostic. So understandably, the distribution doesn't include a secondary tool to create a design document.

The JavaScript support is based on the Mozilla Spidermonkey engine and it has somewhat strict module dependency rules and other limitations that you need to be aware of while writing your design functions.

couchilla is a tool for conveniently bundling design documents for CouchDB, with CommonJS support. It takes view and filter functions from a directory of JavaScript files and outputs a design document JSON.

Directory structure

Here is an example directory structure of a simple design document:

.
├── filters
│   └── quu.js
├── views
│   ├── foo.map.js
│   └── bar.reduce.js
└── validate_doc_update.js

Examples

Map functions

Emit key/value pairs to store them in a view.

views/foo.map.js

export default doc => emit(doc._id, 42)

Reduce functions

Take sum of mapped values:

views/foo.reduce.js

export default (keys, values, rereduce) => {
  if (rereduce) {
    return sum(values)
  } else {
    return values.length
  }
}

Filter functions

Filter by field:

filters/foo.js

export default (doc, req) => {
  if (doc && doc.title && doc.title.startsWith('C')) {
    return true
  }
  return false
}

Validate document update functions

Log incoming requests and respond with forbidden:

export default (newDoc, oldDoc, userCtx, secObj) => {
  log(newDoc)
  log(oldDoc)
  log(userCtx)
  log(secObj)
  throw { forbidden: 'not able now!' }
}

Builtin reduce functions

You can opt to use Erlang native functions using the builtin annotation. For example the sum function above can be rewritten using _sum.

views/foo.reduce.js

/* builtin _sum */

During compilation this will be replaced with a call to the builtin _sum function of CouchDB.

CommonJS

All code should be inside the exported default function, including your require() calls.

views/gamma.map.js

export default doc => {
  const gamma = require('gamma')

  emit(doc._id, gamma(doc.value))
}