.. Licensed under the Apache License, Version 2.0 (the "License"); you may not
.. use this file except in compliance with the License. You may obtain a copy of
.. the License at
..
..   http://www.apache.org/licenses/LICENSE-2.0
..
.. Unless required by applicable law or agreed to in writing, software
.. distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
.. WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
.. License for the specific language governing permissions and limitations under
.. the License.

.. default-domain:: js

.. _ddocs:

===========
Design Docs
===========

In this section we'll show how to write design documents, using the built-in
:ref:`JavaScript Query Server <queryserver_js>`.

But before we start to write our first function, let's take a look at the list
of common objects that will be used during our code journey - we'll be using
them extensively within each function:

- :ref:`Database information object <dbinfo_object>`
- :ref:`Request object <request_object>`
- :ref:`Response object <response_object>`
- :ref:`UserCtx object <userctx_object>`
- :ref:`Database Security object <security_object>`
- :ref:`Guide to JavaScript Query Server <queryserver_js>`

.. _viewfun:

View functions
==============

Views are the primary tool used for querying and reporting on CouchDB databases.

.. _mapfun:

Map functions
-------------

.. function:: mapfun(doc)

   :param doc: Processed document object.

Map functions should take a single argument as document object and optionally
emits paired values also known as `key` and `value`. Since JavaScript
doesn't support generators and ``yield`` statement it is emulated via :func:`emit`:

.. code-block:: javascript

    function(doc){
      if (!doc.tags || !isArray(doc.tags) || !doc.type || doc.type != 'post'){
        return;
      }
      for (var idx in doc.tags){
        emit(doc.tags[idx].toLower(), 1);
      }
    }

In this example the map function produces documents view by tag if they
has `tags` attribute as array and `type` attribute with "post" value. Note that
:func:`emit` function could be called  multiple times, so the same document
will be available by several `keys`.

Also keep in mind that each document is *sealed* to prevent situation when one
map function changes document state and the other one received a modified
version.

For efficiency reasons, documents are passed to a group of map functions -
each document is processed by group of map functions from all views of
related design document. This means that if you trigger index update for one
view in ddoc, all others will get updated too.

Since `1.1.0` release `map` function supports 
:ref:`CommonJS <commonjs>` modules and access to :func:`require` function.

.. _reducefun:

Reduce and rereduce functions
-----------------------------

.. function:: redfun(keys, values[, rereduce])

   :param keys: Array of pairs docid-key for related map function result.
                Always ``null`` if rereduce is running (has ``true`` value).
   :param values: Array of map function result values.
   :param rereduce: Boolean sign of rereduce run.

   :return: Reduces `values`

Reduce functions takes two required arguments of keys and values lists - the
result of the related map function - and optional third one which indicates if
`rereduce` mode is active or not. `Rereduce` is using for additional reduce
values list, so when it is ``true`` there is no information about related `keys`
(first argument is ``null``).

Note, that if produced result by `reduce` function is longer than initial
values list then a Query Server error will be raised. However, this behavior
could be disabled by setting ``reduce_limit`` config option to ``false``:

.. code-block:: ini

   [query_server_config]
   reduce_limit = false

While disabling ``reduce_limit`` might be useful for debug proposes, remember,
that main task of reduce functions is to *reduce* mapped result, not to make it
even bigger. Generally, your reduce function should converge rapidly to a single
value - which could be an array or similar object.

Also CouchDB has three built-in reduce functions. These are implemented in
Erlang and run right inside CouchDB, so they are much faster than the equivalent
JavaScript functions: ``_sum``, ``_count`` and ``_stats``. Their equivalents in
JavaScript below:

.. code-block:: javascript

    // could be replaced by _sum
    function(keys, values){
      sum(values);
    }

    // could be replaced by _count
    function(keys, values, rereduce){
      if (rereduce){
        return sum(values);
      } else {
        return values.length;
      }
    }

    // could be replaced by _stats
    function(keys, values, rereduce){
      return {
        'sum': sum(values),
        'min': Math.min.apply(null, values),
        'max': Math.max.apply(null, values),
        'count': values.length,
        'sumsqr': (function(){
          _sumsqr = 0;
          for(var idx in values){
            _sumsqr += values[idx] * values[idx];
          }
          return _sumsqr;
        })(),
      }
    }

.. note:: **Why don't reduce functions support CommonJS modules?**

   While `map` functions have limited access to stored modules through
   :func:`require` function there is no such feature for `reduce` functions.
   The reason lies deep inside in mechanism how `map` and `reduce` functions
   are processed by Query Server. Let's take a look on `map` functions first:

   #. CouchDB sends all `map` functions for processed design document to
      Query Server.
   #. Query Server handles them one by one, compiles and puts them onto an
      internal stack.
   #. After all `map` functions had been processed, CouchDB will send the
      remaining documents to index one by one.
   #. The Query Server receives the document object and applies it to every function
      from the stack. The emitted results are then joined into a single array and sent
      back to CouchDB.

   Now let's see how `reduce` functions are handled:

   #. CouchDB sends *as single command* list of available `reduce` functions
      with result list of key-value pairs that was previously received as
      result of `map` functions work.
   #. Query Server compiles reduce functions and applies them to key-value
      lists. Reduced result sends back to CouchDB.

   As you may note, `reduce` functions been applied in single shot while
   `map` ones are applied in an iterative way per each document. This means that
   it's possible for `map` functions to precompile CommonJS libraries and use them
   during the entire view processing, but for `reduce` functions it will be
   compiled again and again for each view result reduction, which will lead to
   performance degradation (`reduce` function are already does hard work to make
   large result smaller).


.. _showfun:

Show functions
==============

.. function:: showfun(doc, req)

   :param doc: Processed document, may be omitted.
   :param req: :ref:`Request object <request_object>`.

   :return: :ref:`Response object <response_object>`
   :rtype: object or string

Show functions are used to represent documents in various formats, commonly as
HTML page with nicer formatting. They can also be used to run server-side functions
without requiring a pre-existing document.

Basic example of show function could be:

.. code-block:: javascript

    function(doc, req){
      if (doc){
        return "Hello from " + doc._id + "!";
      } else {
        return "Hello, world!";
      }
    }

Also, there is more simple way to return json encoded data:

.. code-block:: javascript

    function(doc, req){
      return {
        'json': {
          'id': doc['_id'],
          'rev': doc['_rev']
        }
      }
    }


and even files (this one is CouchDB logo):

.. code-block:: javascript

    function(doc, req){
      return {
        'headers': {
          'Content-Type' : 'image/png',
        },
        'base64': ''.concat(
          'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAsV',
          'BMVEUAAAD////////////////////////5ur3rEBn////////////////wDBL/',
          'AADuBAe9EB3IEBz/7+//X1/qBQn2AgP/f3/ilpzsDxfpChDtDhXeCA76AQH/v7',
          '/84eLyWV/uc3bJPEf/Dw/uw8bRWmP1h4zxSlD6YGHuQ0f6g4XyQkXvCA36MDH6',
          'wMH/z8/yAwX64ODeh47BHiv/Ly/20dLQLTj98PDXWmP/Pz//39/wGyJ7Iy9JAA',
          'AADHRSTlMAbw8vf08/bz+Pv19jK/W3AAAAg0lEQVR4Xp3LRQ4DQRBD0QqTm4Y5',
          'zMxw/4OleiJlHeUtv2X6RbNO1Uqj9g0RMCuQO0vBIg4vMFeOpCWIWmDOw82fZx',
          'vaND1c8OG4vrdOqD8YwgpDYDxRgkSm5rwu0nQVBJuMg++pLXZyr5jnc1BaH4GT',
          'LvEliY253nA3pVhQqdPt0f/erJkMGMB8xucAAAAASUVORK5CYII=')
      }
    }

But what if you need to represent data in different formats via a single function?
Functions :func:`registerType` and :func:`provides` are your the best friends in
that question:

.. code-block:: javascript

    function(doc, req){
      provides('json', function(){
        return {'json': doc}
      });
      provides('html', function(){
        return '<pre>' + toJSON(doc) + '</pre>'
      })
      provides('xml', function(){
        return {
          'headers': {'Content-Type': 'application/xml'},
          'body' : ''.concat(
            '<?xml version="1.0" encoding="utf-8"?>\n',
            '<doc>',
            (function(){
              escape = function(s){
                return s.replace(/&quot;/g, '"')
                        .replace(/&gt;/g, '>')
                        .replace(/&lt;/g, '<')
                        .replace(/&amp;/g, '&');
              };
              var content = '';
              for(var key in doc){
                if(!doc.hasOwnProperty(key)) continue;
                var value = escape(toJSON(doc[key]));
                var key = escape(key);
                content += ''.concat(
                  '<' + key + '>',
                  value
                  '</' + key + '>'
                )
              }
              return content;
            })(),
            '</doc>'
          )
        }
      })
      registerType('text-json', 'text/json')
      provides('text-json', function(){
        return toJSON(doc);
      })
    }

This function may return `html`, `json` , `xml` or our custom `text json` format
representation of same document object with same processing rules. Probably,
the `xml` provider in our function needs more care to handle nested objects
correctly, and keys with invalid characters, but you've got the idea!

.. seealso::

   CouchDB Wiki:
    - `Showing Documents <http://wiki.apache.org/couchdb/Formatting_with_Show_and_List#Showing_Documents>`_

   CouchDB Guide:
     - `Show Functions <http://guide.couchdb.org/editions/1/en/show.html>`_


.. _listfun:

List functions
==============

.. function:: listfun(head, req)

   :param head: :ref:`view_head_info_object`
   :param req: :ref:`Request object <request_object>`.

   :return: Last chunk.
   :rtype: string

While :ref:`showfun` are used to customize document presentation, :ref:`listfun`
are used for same purpose, but against :ref:`viewfun` results.

The next list function formats view and represents it as a very simple HTML page:

.. code-block:: javascript

    function(head, req){
      start({
        'headers': {
          'Content-Type': 'text/html'
        }
      });
      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>');
    }

Templates and styles could obviously be used to present data in a nicer
fashion, but this is an excellent starting point. Note that you may also
use :func:`registerType` and :func:`provides` functions in the same
way as for :ref:`showfun`!

.. seealso::

   CouchDB Wiki:
    - `Listing Views with CouchDB 0.10 and later <http://wiki.apache.org/couchdb/Formatting_with_Show_and_List#Listing_Views_with_CouchDB_0.10_and_later>`_

   CouchDB Guide:
    - `Transforming Views with List Functions <http://guide.couchdb.org/draft/transforming.html>`_


.. _updatefun:

Update functions
================

.. function:: updatefun(doc, req)

   :param doc: Update function target document.
   :param req: :ref:`request_object`

   :returns: Two-element array: the first element is the (updated or new)
             document, which is committed to the database. If the first element
             is ``null`` no document will be committed to the database.
             If you are updating an existing, it should already have an ``_id``
             set, and if you are creating a new document, make sure to set its
             ``_id`` to something, either generated based on the input or the
             ``req.uuid`` provided. The second element is the response that will
             be sent back to the caller.

Update handlers are functions that clients can request to invoke server-side
logic that will create or update a document. This feature allows a range of use
cases such as providing a server-side last modified timestamp, updating
individual fields in a document without first getting the latest revision, etc.

When the request to an update handler includes a document ID in the URL, the
server will provide the function with the most recent version of that document.
You can provide any other values needed by the update handler function via the
``POST``/``PUT`` entity body or query string parameters of the request.

The basic example that demonstrates all use-cases of update handlers below:

.. code-block:: javascript

    function(doc, req){
        if (!doc){
            if ('id' in req){
                // 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!']
    }

.. seealso::

   CouchDB Wiki:
    - `Document Update Handlers <http://wiki.apache.org/couchdb/Document_Update_Handlers>`_


.. _filterfun:

Filter functions
================

.. function:: filterfun(doc, req)

   :param doc: Processed document object.
   :param req: :ref:`request_object`
   :return: Boolean value: ``true`` means that `doc` passes the filter rules,
            ``false`` that not.

Filter functions are mostly acts like :ref:`showfun` and :ref:`listfun`: they
formats, but more correctly to say, they *filters* :ref:`changes feed<changes>`.

Classic filters
---------------

By default the changes feed emits all database documents changes. But if you're
waiting for some special changes, processing all documents is inefficient.

Filters are special design document functions that allows changes feed to emit
only specific documents that pass filter rules.

Lets assume that our database is a mailbox and we need to to handle only new mails
(documents with status `new`) events. Assuming that, our filter function
will looks like next one:

.. code-block:: javascript

  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 must return ``true`` in fact if document passed all defined
rules. Now, if you apply this function to changes feed it will emit only changes
about "new mails"::

    GET /somedatabase/_changes?filter=mailbox/new_mail HTTP/1.1

.. code-block:: javascript

    {"results":[
    {"seq":1,"id":"df8eca9da37dade42ee4d7aa3401f1dd","changes":[{"rev":"1-c2e0085a21d34fa1cecb6dc26a4ae657"}]},
    {"seq":7,"id":"df8eca9da37dade42ee4d7aa34024714","changes":[{"rev":"1-29d748a6e87b43db967fe338bcb08d74"}]},
    ],
    "last_seq":27}

Note, that ``last_seq`` number is 27, but we'd received only two records.
Seems like any other changes was about documents that hasn't passed our filter.

Probably, we also need to filter changes feed of our mailbox not only by single
status value: we're also interested in statuses like "spam" to update
spam-filter heuristic rules, "outgoing" to let mail daemon actually send mails
and so on. Creating a lot of similar functions that actually does similar work
isn't good idea - so we need dynamic filter to go.

If you have noted, filter functions takes second argument as
:ref:`request <request_object>` object - it allows to create dynamic filters
based on query parameters, :ref:`user context <userctx_object>` and more.

The dynamic version of our filter now will be next:

.. code-block:: javascript

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

and now we have pass `status` query parameter in request to let filter match
only required documents::

    GET /somedatabase/_changes?filter=mailbox/by_status&status=new HTTP/1.1

.. code-block:: javascript

    {"results":[
    {"seq":1,"id":"df8eca9da37dade42ee4d7aa3401f1dd","changes":[{"rev":"1-c2e0085a21d34fa1cecb6dc26a4ae657"}]},
    {"seq":7,"id":"df8eca9da37dade42ee4d7aa34024714","changes":[{"rev":"1-29d748a6e87b43db967fe338bcb08d74"}]},
    ],
    "last_seq":27}

and we can change filter behavior with easy::

    GET /somedatabase/_changes?filter=mailbox/by_status&status=spam HTTP/1.1

.. code-block:: javascript

    {"results":[
    {"seq":11,"id":"8960e91220798fc9f9d29d24ed612e0d","changes":[{"rev":"3-cc6ff71af716ddc2ba114967025c0ee0"}]},
    ],
    "last_seq":27}


Combining filters with `continuous` feed allows to create powerful event-driven
systems.

View filters
------------

View filters are the same as above, with one small difference: they use
views `map` function instead to `filter` one to process the changes feed. Each
time when a key-value pair could be emitted, a change is returned. This allows
to avoid creating filter functions that are mostly does same works as views.

To use them just specify `_view` value for ``filter`` parameter and
`designdoc/viewname` for ``view`` one::

    GET /somedatabase/_changes?filter=_view&view=dname/viewname  HTTP/1.1

.. note::

   Since view filters uses `map` functions as filters, they can't show any
   dynamic behavior since :ref:`request object<request_object>` is not
   available.

.. seealso::

   CouchDB Guide:
    - `Guide to filter change notification <http://guide.couchdb.org/draft/notifications.html#filters>`_

   CouchDB Wiki:
    - `Filtered replication <http://wiki.apache.org/couchdb/Replication#Filtered_Replication>`_


.. _vdufun:

Validate document update functions
==================================

.. function:: validatefun(newDoc, oldDoc, userCtx, secObj)

   :param newDoc: New version of document that will be stored.
   :param oldDoc: Previous version of document that is already stored.
   :param userCtx: :ref:`userctx_object`
   :param secObj: :ref:`security_object`

   :throws: ``forbidden`` error to gracefully prevent document storing.

To perform validate operations on document saving there is a special design
function type called `validate_doc_update`.

Instead of thousands words take a look at the next example of validate
function - this function is used in ``_design/_auth`` ddoc from `_users`
database to control users documents required field set and modification
permissions:

.. code-block:: javascript

    function(newDoc, oldDoc, userCtx, secObj) {
        if (newDoc._deleted === true) {
            // allow deletes by admins and matching users
            // without checking the other fields
            if ((userCtx.roles.indexOf('_admin') !== -1) ||
                (userCtx.name == oldDoc.name)) {
                return;
            } else {
                throw({forbidden: 'Only admins may delete other user docs.'});
            }
        }

        if ((oldDoc && oldDoc.type !== 'user') || newDoc.type !== 'user') {
            throw({forbidden : 'doc.type must be user'});
        } // we only allow user docs for now

        if (!newDoc.name) {
            throw({forbidden: 'doc.name is required'});
        }

        if (!newDoc.roles) {
            throw({forbidden: 'doc.roles must exist'});
        }

        if (!isArray(newDoc.roles)) {
            throw({forbidden: 'doc.roles must be an array'});
        }

        if (newDoc._id !== ('org.couchdb.user:' + newDoc.name)) {
            throw({
                forbidden: 'Doc ID must be of the form org.couchdb.user:name'
            });
        }

        if (oldDoc) { // validate all updates
            if (oldDoc.name !== newDoc.name) {
                throw({forbidden: 'Usernames can not be changed.'});
            }
        }

        if (newDoc.password_sha && !newDoc.salt) {
            throw({
                forbidden: 'Users with password_sha must have a salt.' +
                    'See /_utils/script/couch.js for example code.'
            });
        }

        var is_server_or_database_admin = function(userCtx, secObj) {
            // see if the user is a server admin
            if(userCtx.roles.indexOf('_admin') !== -1) {
                return true; // a server admin
            }

            // see if the user a database admin specified by name
            if(secObj && secObj.admins && secObj.admins.names) {
                if(secObj.admins.names.indexOf(userCtx.name) !== -1) {
                    return true; // database admin
                }
            }

            // see if the user a database admin specified by role
            if(secObj && secObj.admins && secObj.admins.roles) {
                var db_roles = secObj.admins.roles;
                for(var idx = 0; idx < userCtx.roles.length; idx++) {
                    var user_role = userCtx.roles[idx];
                    if(db_roles.indexOf(user_role) !== -1) {
                        return true; // role matches!
                    }
                }
            }

            return false; // default to no admin
        }

        if (!is_server_or_database_admin(userCtx, secObj)) {
            if (oldDoc) { // validate non-admin updates
                if (userCtx.name !== newDoc.name) {
                    throw({
                        forbidden: 'You may only update your own user document.'
                    });
                }
                // validate role updates
                var oldRoles = oldDoc.roles.sort();
                var newRoles = newDoc.roles.sort();

                if (oldRoles.length !== newRoles.length) {
                    throw({forbidden: 'Only _admin may edit roles'});
                }

                for (var i = 0; i < oldRoles.length; i++) {
                    if (oldRoles[i] !== newRoles[i]) {
                        throw({forbidden: 'Only _admin may edit roles'});
                    }
                }
            } else if (newDoc.roles.length > 0) {
                throw({forbidden: 'Only _admin may set roles'});
            }
        }

        // no system roles in users db
        for (var i = 0; i < newDoc.roles.length; i++) {
            if (newDoc.roles[i][0] === '_') {
                throw({
                    forbidden:
                    'No system roles (starting with underscore) in users db.'
                });
            }
        }

        // no system names as names
        if (newDoc.name[0] === '_') {
            throw({forbidden: 'Username may not start with underscore.'});
        }

        var badUserNameChars = [':'];

        for (var i = 0; i < badUserNameChars.length; i++) {
            if (newDoc.name.indexOf(badUserNameChars[i]) >= 0) {
                throw({forbidden: 'Character `' + badUserNameChars[i] +
                        '` is not allowed in usernames.'});
            }
        }
    }

.. note::

   The ``return`` statement used only for function, it has no impact on 
   the validation process.

.. seealso::

   CouchDB Guide:
    - `Validation Functions <http://guide.couchdb.org/editions/1/en/validation.html>`_

   CouchDB Wiki:
    - `Document Update Validation <http://wiki.apache.org/couchdb/Document_Update_Validation>`_
