You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by dc...@apache.org on 2012/11/18 20:19:39 UTC

[2/6] git commit: Complete article about how to write design documents.

Complete article about how to write design documents.

Also move filter functions description away from changes article and let it just reference to ddocs section.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/8cbe836e
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/8cbe836e
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/8cbe836e

Branch: refs/heads/docs
Commit: 8cbe836eecd1a5b70bf62cc527f8efe894621bfc
Parents: 858c1e0
Author: Alexander Shorin <kx...@gmail.com>
Authored: Tue Nov 6 01:38:52 2012 +0400
Committer: Dave Cottlehuber <dc...@apache.org>
Committed: Sun Nov 18 20:18:33 2012 +0100

----------------------------------------------------------------------
 share/doc/src/changes.rst  |  123 +-------
 share/doc/src/commonjs.rst |    2 +
 share/doc/src/ddocs.rst    |  704 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 708 insertions(+), 121 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/8cbe836e/share/doc/src/changes.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/changes.rst b/share/doc/src/changes.rst
index f0d61ed..1fcec78 100644
--- a/share/doc/src/changes.rst
+++ b/share/doc/src/changes.rst
@@ -77,8 +77,9 @@ Notes:
         [httpd]
         changes_timeout=#millisecs
 
-(7) Reference a filter function from a design document to selectively get
-    updates. See the `section in the book`_ for more information.
+(7) Reference to a :ref:`filter function <filterfun>` from a design document
+    that will filter whole stream emitting only filtered events.
+    See the `section in the book`_ for more information.
 
 (8) Include the associated document with each result. If there are conflicts,
     only the winning revision is returned.
@@ -178,122 +179,4 @@ results.
 Obviously, `... tum tee tum ...` does not appear in the actual response, but
 represents a long pause before the change with seq 6 occurred.  
 
-Filters
-=======
-
-Classic filters
----------------
-
-By default changes feed emits all database documents changes. But if you're
-waiting for some special changes it's not optimal to process each record.
-
-Filters are special design document functions that allows changes feed to emit
-only specific documents that passed filter rules.
-
-Let assume that our database is mailbox and we need to listen changes to handle
-only new mails (documents with status `new`). Assuming that, our filter function
-would 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 function must return true in fact if document passed all defined rules.
-Now, if you apply this function to changes feed, you're changes feed will emit
-only changes about "new mail"::
-
-
-    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 hadn't passed our filter.
-
-Probably, we also need to filter changes feed of our mailbox not only by single
-status value: we 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 same 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 classic one with one small difference: they used
-view map function instead to filter changes feed. Each time a value 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 are uses map function as filter they couldn't have
-   dynamic behavior since :ref:`request object<request_object>` is not available
-
 .. _section in the book: http://books.couchdb.org/relax/reference/change-notifications

http://git-wip-us.apache.org/repos/asf/couchdb/blob/8cbe836e/share/doc/src/commonjs.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/commonjs.rst b/share/doc/src/commonjs.rst
index 81ef310..ff31095 100644
--- a/share/doc/src/commonjs.rst
+++ b/share/doc/src/commonjs.rst
@@ -10,6 +10,8 @@
 .. License for the specific language governing permissions and limitations under
 .. the License.
 
+.. _commonjs:
+
 CommonJS support for map functions
 ==================================
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/8cbe836e/share/doc/src/ddocs.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/ddocs.rst b/share/doc/src/ddocs.rst
index 4dd9466..89fa62c 100644
--- a/share/doc/src/ddocs.rst
+++ b/share/doc/src/ddocs.rst
@@ -10,10 +10,712 @@
 .. License for the specific language governing permissions and limitations under
 .. the License.
 
+.. default-domain:: js
+
 .. _ddocs:
 
+===========
 Design Docs
 ===========
 
-To be added: information on writing design docs, including views.
+In this section we'll show how to write design document in context of the
+:ref:`JavaScript Query Server <queryserver_js>`.
+
+But before we start to write our first function, take a look at the list of
+common objects that will be used during our code journey - we'll made big deal
+with them for 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 emits pair
+values also known as `key` and `value`. Since JavaScript doesn't support
+generators and ``yield`` statement it got 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 for multiple times, so 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 his modified
+version.
+
+Yes, as was mentioned above, map function doesn't executes alone. Instead of
+that 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 up-to-dated too.
+
+Since `1.1.0` release `map` function gains support for
+: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 signs 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 an 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.
+
+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 reduce functions has no support of CommonJS modules?**
+
+   While `map` functions has 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 into inner stack.
+   #. After all `map` functions had been proceeded, CouchDB starts to send
+      reamain documents to index one by one.
+   #. Query Server handles document object and apply to him every function from
+      inner stack. Their emitted result joins into single array and pulls 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 applies in iterative way per each document. This means that
+   it's possible for `map` functions to precompile CommonJS library and use it
+   during whole view processing, but for `reduce` functions it will be
+   compiled again and again for each view result reducing which may leads 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.
+
+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 by 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 in more care to handle nested objects
+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 propose, but against :ref:`viewfun` result.
+
+Next list function formats view and represent in as 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>');
+    }
+
+Probably, you'd better to think about templates, styles and more other things to
+show data in more nicer way, but this is good point to start from. Note, that
+you may use there :func:`registerType` and :func:`provides` functions in 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 changes feed emits all database documents changes. But if you're
+waiting for some special changes this is not optimal to process each record.
+
+Filters are special design document functions that allows changes feed to emit
+only specific documents that passed filter rules.
+
+Lets assume that our database is 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 classic one with one small difference: they uses
+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 are uses `map` functions as filter they couldn't
+   have 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 special design
+function type called `validate_doc_update`.
+
+Instead of thousands words take a look on next good example of validate
+function - this function is uses 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 exiting and it doesn't
+   controls 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>`_