CouchDB design document bundler
- January 2, 2023
- couchdb, javascript and tool
couchilla is a bundler for packing design documents for CouchDB with CommonJS support.
Overview
In CouchDB, design documents are special database entries that contain JavaScript functions, such as view and update functions. These functions, executed on demand, generate secondary indexes, often termed MapReduce views.
JavaScript support in CouchDB is based on the Mozilla SpiderMonkey engine (and, starting with version 3.4.1, also QuickJS). However, because design functions are language-independent, CouchDB does not include a dedicated tool for creating them.
couchilla creates design documents with CommonJS support. It reads view and filter functions from JavaScript files in a directory and generates a design document in JSON.
Directory structure
Here's an example directory structure:
.
├── filters
│ └── quu.js
├── views
│ ├── foo.map.js
│ └── bar.reduce.js
└── validate_doc_update.js
- View functions reside in the
views
directory. Files with.map.js
(or simply.js
) are converted into map functions.- Reduce functions are defined in files with
.reduce.js
extensions.
- Reduce functions are defined in files with
- Filter functions belong in the
filters
directory.
View functions
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
}
}
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.
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!' }
}
Requiring other modules
All code, including require()
statements, must be enclosed within the exported default function.
views/gamma.map.js
export default doc => {
const gamma = require('gamma')
emit(doc._id, gamma(doc.value))
}