Design Documents

Important: All Cloudant documentation has moved to the IBM Bluemix platform. You can find the new content here, and the Design Documents topic in particular here.

Content on this page will no longer be updated (Jan 31st, 2017).

Instead of storing data in a document, you might also have special documents that store other content, such as functions. The special documents are called “design documents”.

Design documents are documents that have an _id beginning with _design/. They can be read and updated in the same way as any other document in the database. Cloudant reads specific fields and values of design documents as functions. Design documents are used to build indexes, validate updates, and format query results.

Creating or updating a design document

To create a design document, upload it to the specified database.

In these examples, $VARIABLES might refer to standard and design documents. To distinguish between them, standard documents have an _id indicated by $DOCUMENT_ID, while design documents have an _id indicated by $DESIGN_ID.

The structure of design document is as follows:

Copying a Design Document

You can copy the latest version of a design document to a new document by specifying the base document and target document. The copy is requested using the COPY HTTP request.

COPY /recipes/_design/recipes HTTP/1.1
Content-Type: application/json
Destination: /recipes/_design/recipelist
curl "https://$ACCOUNT.cloudant.com/recipes/_design/recipes" \
     -X COPY \
     -H 'Content-Type: application/json' \
     -H 'Destination: /recipes/_design/recipelist'
{
  "id": "recipes/_design/recipelist",
  "rev": "1-9c65296036141e575d32ba9c034dd3ee"
}

An example request to copy the design document recipes to the new design document recipelist produces a response containing the ID and revision of the new document.

The structure of the copy command

The source design document is specified on the request line, with the Destination HTTP Header of the request specifying the target document.

Copying from a specific revision

COPY /recipes/_design/recipes?rev=1-e23b9e942c19e9fb10ff1fde2e50e0f5 HTTP/1.1
Content-Type: application/json
Destination: recipes/_design/recipelist
curl "https://$ACCOUNT.cloudant.com/recipes/_design/recipes?rev=1-e23b9e942c19e9fb10ff1fde2e50e0f5" \
     -X COPY \
     -H 'Content-Type: application/json' \
     -H 'Destination: /recipes/_design/recipelist'

To copy from a specific version, add the rev argument to the query string.

The new design document is created using the specified revision of the source document.

Copying to an existing design document

COPY /recipes/_design/recipes
Content-Type: application/json
Destination: recipes/_design/recipelist?rev=1-9c65296036141e575d32ba9c034dd3ee
curl "https://$ACCOUNT.cloudant.com/recipes/_design/recipes" \
     -X COPY \
     -H 'Content-Type: application/json' \
     -H 'Destination: /recipes/_design/recipelist?rev=1-9c65296036141e575d32ba9c034dd3ee'
{
    "id" : "recipes/_design/recipes",
    "rev" : "2-55b6a1b251902a2c249b667dab1c6692"
}

To copy to an existing document, specify the current revision string for the target document, using the rev parameter to the Destination HTTP Header string.

The return value is the new revision of the copied document.

Deleting a design document

DELETE /recipes/_design/recipes?rev=2-ac58d589b37d01c00f45a4418c5a15a8 HTTP/1.1
curl "https://$ACCOUNT.cloudant.com/recipes/_design/recipes?rev=2-ac58d589b37d01c00f45a4418c5a15a8" \
     -X DELETE
{
  "id": "recipe/_design/recipes",
  "ok": true,
  "rev": "3-7a05370bff53186cb5d403f861aca154"
}

You can delete an existing design document. Deleting a design document also deletes all of the associated view indexes, and recovers the corresponding space on disk for the indexes in question.

To delete successfully, you must specify the current revision of the design document using the rev query argument.

The structure of the delete command

Views

An important use of design documents is for creating views. These are discussed in more detail here.

Rewrite rules

{
    "rewrites": [
        {
            "from": "/",
            "to": "index.html",
            "method": "GET",
            "query": {}
        },
        {
            "from": "/foo/:var",
            "to": "/foo",
            "method": "GET",
            "query": {"v": "var"}
        }
    ]
}

A design document can contain rules for URL rewriting, by using an array in the rewrites field. Requests that match the rewrite rules must have a URL path that starts with /$DATABASE/_design/doc/_rewrite.

Each rule is a JSON object with 4 fields:

Field Description
from A path relative to /$DATABASE/_design/doc/_rewrite, used to match URLs to rewrite rules. Path elements that start with a : are treated as variables and match any string that does not contain a /. A * can only appear at the end of the string, and matches any string - including slashes.
to The path (relative to /$DATABASE/_design/doc/ and not including the query part of the URL) that is the result of the rewriting step. Variables captured in from can be used in to. * can also be used and contains everything captured by the pattern in from.
method The HTTP method that should be matched on.
query The query part of the resulting URL. This is a JSON object containing the key/value pairs of the query.

Example rewrite rules

Rule URL Rewrite to Tokens
{"from": "/a/b", "to": "/some/"} /$DATABASE/_design/doc/_rewrite/a/b?k=v /$DATABASE/_design/doc/some?k=v k = v
{"from": "/a/b", "to": "/some/:var"} /$DATABASE/_design/doc/_rewrite/a/b /$DATABASE/_design/doc/some/b?var=b var = b
{"from": "/a", "to": "/some/*"} /$DATABASE/_design/doc/_rewrite/a /$DATABASE/_design/doc/some  
{"from": "/a/*", "to": "/some/*} /$DATABASE/_design/doc/_rewrite/a/b/c /$DATABASE/_design/doc/some/b/c  
{"from": "/a", "to": "/some/*"} /$DATABASE/_design/doc/_rewrite/a /$DATABASE/_design/doc/some  
{"from": "/a/:foo/*","to": "/some/:foo/*"} /$DATABASE/_design/doc/_rewrite/a/b/c /$DATABASE/_design/doc/some/b/c?foo=b foo = b
{"from": "/a/:foo", "to": "/some", "query": { "k": ":foo" }} /$DATABASE/_design/doc/_rewrite/a/b /$DATABASE/_design/doc/some/?k=b&foo=b foo =:= b
{"from": "/a", "to": "/some/:foo" } /$DATABASE/_design/doc/_rewrite/a?foo=b /$DATABASE/_design/doc/some/b&foo=b foo = b

Indexes

All queries operate on pre-defined indexes defined in design documents. These indexes are:

For example, to create a design document used for searching, you must ensure that two conditions are true:

  1. You have identified the document as a design document by having an _id starting with _design/.
  2. A search index has been created within the document by updating the document with the appropriate field or by creating a new document containing the search index.

As soon as the search index design document exists and the index has been built, you can make queries using it.

For more information about search indexing, refer to the search section of this documentation.

General notes on functions in design documents

Functions in design documents are run on multiple nodes for each document and might be run several times. To avoid inconsistencies, they need to be idempotent, meaning they need to behave identically when run multiple times and/or on different nodes. In particular, you should avoid using functions that generate random numbers or return the current time.

List Functions

{
  "_id": "_design/list_example",
  "lists": {
    "FUNCTION_NAME": "function (head, req) { ... }"
  }
}
function (head, req) {
  // specify our headers
  start({
    headers: {
      "Content-Type": 'text/html'
    }
  });
  // send the respond, line by line
  send('<html><body><table>');
  send('<tr><th>ID</th><th>Key</th><th>Value</th></tr>')
  while(row = getRow()){
    send(''.concat(
      '<tr>',
      '<td>' + toJSON(row.id) + '</td>',
      '<td>' + toJSON(row.key) + '</td>',
      '<td>' + toJSON(row.value) + '</td>',
      '</tr>'
    ));
  }
  send('</table></body></html>');
}
GET /$DATABASE/$DESIGN_ID/_list/$LIST_FUNCTION/$MAPREDUCE_INDEX HTTP/1.1
curl "https://$ACCOUNT.cloudant.com/$DATABASE/$DESIGN_ID/_list/$LIST_FUNCTION/$MAPREDUCE_INDEX" \
var nano = require('nano');
var db = account.use($DATABASE);

db.view_with_list($DESIGN_ID, $MAPREDUCE_INDEX, $LIST_FUNCTION, function (err, body, headers) {
  if (!err) {
    console.log(body);
  }
});

Use list functions to customize the format of MapReduce query results. They are used when you want to access Cloudant directly from a browser, and need data to be returned in a different format, such as HTML. You can add any query parameters to the request that would normally be used for a view request. Instead of using a MapReduce index, you can also use _all_docs.

List functions require two arguments: head and req.

When you define a list function, you use it by making a GET request to https://<account>.cloudant.com/<databases>/<design_id>/_list/<list_function>/<mapreduce_index>. In this request:

The other parameters are the same query parameters described here.

Field Description
total_rows Number of documents in the view
offset Offset where the document list started

req

Field Description
body Request body data as string. If the request method is GET this field contains the value “undefined”. If the method is DELETE or HEAD the value is “” (empty string).
cookie Cookies object.
form Form data object. Contains the decoded body as key-value pairs if the Content-Type header was application/x-www-form-urlencoded.
headers Request headers object.
id Requested document id string if it was specified or null otherwise.
info Database information
method Request method as string or array. String value is a method as one of: HEAD, GET, POST, PUT, DELETE, OPTIONS, and TRACE. Otherwise it will be represented as an array of char codes.
path List of requested path sections.
peer Request source IP address.
query URL query parameters object. Note that multiple keys are not supported and the last key value suppresses others.
requested_path List of actual requested path section.
raw_path Raw requested path string.
secObj The database’s security object
userCtx Context about the currently authenticated user, specifically their name and roles within the current database.
uuid A generated UUID

Show Functions

{
  "_id": "_design/show_example",
  "shows": {
    "FUNCTION_NAME": "function (doc, req) { ... }"
  }
}
function (doc, req) {
  if (doc) {
    return "Hello from " + doc._id + "!";
  } else {
    return "Hello, world!";
  }
}
GET /$DATABASE/<design_id>/_show/<show_function>/<document_id http>/1.1
Host: <account>.cloudant.com
curl https://$ACCOUNT.cloudant.com/$DATABASE/$DESIGN_ID/_show/$SHOW_FUNCTION/$DOCUMENT_ID \
var nano = require('nano');
var account = nano("https://"+$ACCOUNT+".cloudant.com");
var db = account.use($DATABASE);

db.show($DESIGN_ID, $SHOW_FUNCTION, $DOCUMENT_ID, function (err, body) {
  if (!err) {
    console.log(body);
  }
});

Show functions are similar to list functions but are used to format individual documents. They are used when you want to access Cloudant directly from a browser, and need data to be returned in a different format, such as HTML.

Show functions receive two arguments: doc, and req. doc is the document requested by the show function.

When you have defined a show function, you query it with a GET request to https://$DATABASE.cloudant.com/$DATABASE/<design_id>/_show/<show_function>/<document_id>, where <show_function> is the name of the function that is applied to the document that has <design_id> as its _id.

Update Handlers

{
  "_id": "_design/update_example",
  "updates": {
    "UPDATE_HANDLER_NAME": "function (doc, req) { ... }"
  }
}
function(doc, req){
  if (!doc){
    if ('id' in req && req.id){
      // create new document
      return [{_id: req.id}, 'New World']
    }
    // change nothing in database
    return [null, 'Empty World']
  }
  doc.world = 'hello';
  doc.edited_by = req.userCtx.name
  return [doc, 'Edited World!']
}
POST /$DATABASE/<design_id>/_update/<update_handler http>/1.1
Content-Type: application/json
curl "https://$ACCOUNT.cloudant.com/$DATABASE/$DESIGN_ID/_update/$UPDATE_HANDLER" \
     -X POST \
     -H 'Content-Type: application/json' \
     -d "$JSON"
var nano = require('nano');
var account = nano("https://"+$ACCOUNT+".cloudant.com");
var db = account.use($DATABASE);

db.atomic($DESIGN_ID, $UPDATE_HANDLER, $DOCUMENT_ID, $JSON, function (err, body) {
  if (!err) {
    console.log(body);
  }
});

Update handlers are custom functions that live on Cloudant’s server that will create or update a document. This can, for example, provide server-side modification timestamps, and document updates to individual fields without the latest revision.

Update handlers receive two arguments: doc and req. If a document ID is provided in the request to the update handler, then doc will be the document corresponding with that ID. If no ID was provided, doc will be null.

Update handler functions must return an array of two elements, the first being the document to save (or null, if you don’t want to save anything), and the second being the response body.

Here’s how to query update handlers:

Method URL
POST https://$ACCOUNT.cloudant.com/$DATABASE/<design_id>/_update>/<update_handler>
PUT https://$ACCOUNT.cloudant.com/$DATABASE/<design_id>/_update>/<update_handler>/<document_id>

Where <design_id> is the _id of the document defining the update handler, $UPDATE_HANDLER is the name of the update handler, and <document_id> is the _id of the document you want the handler to, well, handle.

Filter Functions

{
  "_id":"_design/FILTER_EXAMPLE",
  "filters": {
    "FILTER_EXAMPLE": "function (doc, req) { ... }"
  }
}

Filter functions are design documents that enable you to filter the changes feed. They work by applying tests to each of the objects included in the changes feed. If any of the function tests fail, the object is ‘removed’ or ‘filtered’ from the feed. If the function returns a true result when applied to a change, the change remains in the feed. Therefore, filter functions let you ‘remove’ or ‘ignore’ changes you don’t want to monitor.

function(doc, req){
  // we need only `mail` documents
  if (doc.type != 'mail'){
    return false;
  }
  // we're interested only in `new` ones
  if (doc.status != 'new'){
    return false;
  }
  return true; // passed!
}

Filter functions require two arguments: doc and req.

The doc argument represents the document being tested for filtering.

The req argument contains additional information about the HTTP request. It enables you to create filter functions that are more dynamic, because they are based on additional factors such as query parameters or the user context.

For example, you could control aspects of the filter function tests by using dynamic values provided as part of the HTTP request. In many filter function use cases, however, only the doc parameter is used.

More details about the req parameter are available here.

GET /$DATABASE/_changes?filter=<design_id>/<filter_function http>/1.1
curl "https://$ACCOUNT.cloudant.com/$DATABASE/_changes?filter=$DESIGN_ID/$FILTER_FUNCTION" \

To apply a filter function to the changes feed, include the filter parameter in the _changes query, providing the name of the filter to use.

GET /$DATABASE/_changes?filter=<design_id>/<filter_function&status=new http>/1.1
curl "https://$ACCOUNT.cloudant.com/$DATABASE/_changes?filter=$DESIGN_ID/$FILTER_FUNCTION&status=new" \
function(doc, req){
  // we need only `mail` documents
  if (doc.type != 'mail'){
    return false;
  }
  // we're interested only in `new` ones
  if (doc.status != req.query.status){
    return false;
  }
  return true; // passed!
}

The req argument gives you access to aspects of the HTTP request using the query property.

Predefined filter functions

A number of predefined filter functions are available:

The _design filter

GET /$DATABASE/_changes?filter=_design HTTP/1.1
curl "https://$ACCOUNT.cloudant.com/$DATABASE/_changes?filter=_design" \
     -u "$USERNAME:$PASSWORD"
{
    ...
    "results": [
        {
            "changes": [
                {
                    "rev": "10-304...4b2"
                }
            ],
            "id": "_design/ingredients",
            "seq": "8-g1A...gEo"
        },
        {
            "changes": [
                {
                    "rev": "123-6f7...817"
                }
            ],
            "deleted": true,
            "id": "_design/cookbook",
            "seq": "9-g1A...4BL"
        },
        ...
    ]
}

The _design filter accepts changes only for design documents within the requested database.

The filter does not require any arguments.

Changes are listed for all the design documents within the database.

The _doc_ids filter

POST /$DATABASE/_changes?filter=_doc_ids HTTP/1.1
curl "https://$ACCOUNT.cloudant.com/$DATABASE/_changes?filter=_doc_ids" \
     -u "$USERNAME:$PASSWORD"
{
    "doc_ids": [
        "ExampleID"
    ]
}
{
    "last_seq": "5-g1A...o5i",
    "pending": 0,
    "results": [
        {
            "changes": [
                {
                    "rev": "13-bcb...29e"
                }
            ],
            "id": "ExampleID",
            "seq":  "5-g1A...HaA"
        }
    ]
}

The _doc-ids filter accepts only changes for documents with specified IDs. The IDs are specified in a doc_ids parameter, or within a JSON document supplied as part of the original request.

The _selector filter

POST /$DATABASE/_changes?filter=_selector HTTP/1.1
curl "https://$ACCOUNT.cloudant.com/$DATABASE/_changes?filter=_selector" \
     -u "$USERNAME:$PASSWORD"
{
    "selector": {
        "_id": {
          "$regex": "^_design/"
        }
    }
}
{
    "last_seq": "11-g1A...OaA",
    "pending": 0,
    "results": [
        {
            "changes": [
                {
                  "rev": "10-304...4b2"
                }
            ],
            "id": "_design/ingredients",
            "seq": "8-g1A...gEo"
        },
        {
            "changes": [
                {
                  "rev": "123-6f7...817"
                }
            ],
            "deleted": true,
            "id": "_design/cookbook",
            "seq": "9-g1A...4BL"
        },
        {
            "changes": [
                {
                  "rev": "6-5b8...8f3"
                }
            ],
            "deleted": true,
            "id": "_design/meta",
            "seq": "11-g1A...Hbg"
        }
    ]
}

The _selector filter accepts only changes for documents which match a specified selector, defined using the same selector syntax used for _find.

For more examples showing use of this filter, see the information on selector syntax.

The _view filter

GET /$DATABASE/_changes?filter=_view&view=$DESIGNDOC/$VIEWNAME HTTP/1.1
curl "https://$ACCOUNT.cloudant.com/$DATABASE/_changes?filter=_view&view=$DESIGNDOC/$VIEWNAME" \
     -u "$USERNAME:$PASSWORD"
{
    "last_seq": "5-g1A...o5i",
    "results": [
        {
            "changes": [
                {
                  "rev": "13-bcb...29e"
                }
            ],
            "id": "ExampleID",
            "seq":  "5-g1A...HaA"
        }
    ]
}

The _view filter allows you to use an existing map function as the filter.

If the map function emits any output as a result of processing a given document, then the filter considers the document to be allowed and so includes it in the list of documents that have changed.

Update Validators

{
  "_id": "_design/validator_example",
  "validate_doc_update": "function(newDoc, oldDoc, userCtx, secObj) { ... }"
}
function(newDoc, oldDoc, userCtx, secObj) {
  if (newDoc.address === undefined) {
     throw({forbidden: 'Document must have an address.'});
  }
}
{
  "error": "forbidden",
  "reason": "Document must have an address."
}

Update validators evaluate whether a document should be written to disk when insertions and updates are attempted. They do not require a query because they implicitly run during this process. If a change is rejected, the update validator responds with a custom error.

Update validators get four arguments:

Update validators do not apply when a design document is updated by an admin user, so that admins can never accidentally lock themselves out.

Retrieving information about a design document

There are two endpoints available that provide you with more information: _info and _search_info.

GET /recipes/_design/recipesdd/_info HTTP/1.1
curl "https://$ACCOUNT.cloudant.com/recipes/_design/recipesdd/_info" \
{
   "name" : "recipesdd",
   "view_index": {
      "compact_running": false,
      "updater_running": false,
      "language": "javascript",
      "purge_seq": 10,
      "waiting_commit": false,
      "waiting_clients": 0,
      "signature": "fc65594ee76087a3b8c726caf5b40687",
      "update_seq": 375031,
      "disk_size": 16491
   }
}

Obtains information about a given design document, including the index, index size and current status of the design document and associated index information.

The individual fields in the returned JSON structure are as follows:

GET /foundbite/_design/app/_search_info/description HTTP/1.1
curl "https://$ACCOUNT.cloudant.com/foundbite/_design/app/_search_info/description" \
{
    "name": "_design/app/description",
    "search_index": {
        "pending_seq": 63,
        "doc_del_count": 3,
        "doc_count": 10,
        "disk_size": 9244,
        "committed_seq": 63
    }
}

Obtains information about a search specified within a given design document.

The individual fields in the returned JSON structure are as follows: