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

[3/6] git commit: Add article about queryservers: JavaScript and Erlang one.

Add article about queryservers: JavaScript and Erlang one.


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

Branch: refs/heads/docs
Commit: 858c1e05b517301740b21a0e310027b35e963412
Parents: 56642eb
Author: Alexander Shorin <kx...@gmail.com>
Authored: Tue Nov 6 01:36:30 2012 +0400
Committer: Dave Cottlehuber <dc...@apache.org>
Committed: Sun Nov 18 20:18:23 2012 +0100

----------------------------------------------------------------------
 share/doc/src/index.rst        |    1 +
 share/doc/src/queryservers.rst |  482 +++++++++++++++++++++++++++++++++++
 2 files changed, 483 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/858c1e05/share/doc/src/index.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/index.rst b/share/doc/src/index.rst
index 48b563e..5712d7c 100644
--- a/share/doc/src/index.rst
+++ b/share/doc/src/index.rst
@@ -33,6 +33,7 @@ Contents:
     config_reference
     replication
     ddocs
+    queryservers
     commonjs
     errors
     changes

http://git-wip-us.apache.org/repos/asf/couchdb/blob/858c1e05/share/doc/src/queryservers.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/queryservers.rst b/share/doc/src/queryservers.rst
new file mode 100644
index 0000000..cfeee75
--- /dev/null
+++ b/share/doc/src/queryservers.rst
@@ -0,0 +1,482 @@
+.. 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
+
+=============
+Query servers
+=============
+
+.. _queryserver_js:
+
+JavaScript
+==========
+
+.. note::
+   While every design function has access to all described there objects
+   the table below describes reasonable usage cases. For example, you may
+   use :func:`emit` in :ref:`listfun`, but this is useless while using
+   :func:`getRow` in :ref:`mapfun` will made it broken.
+
++--------------------------------+---------------------------------------------+
+| JS Function                    | Reasonable to use in design doc functions   |
++================================+=============================================+
+| :func:`emit`                   | :ref:`mapfun`                               |
++--------------------------------+---------------------------------------------+
+| :func:`getRow`                 | :ref:`listfun`                              |
++--------------------------------+---------------------------------------------+
+| :data:`JSON`                   | any                                         |
++--------------------------------+---------------------------------------------+
+| :func:`isArray`                | any                                         |
++--------------------------------+---------------------------------------------+
+| :func:`log`                    | any                                         |
++--------------------------------+---------------------------------------------+
+| :func:`provides`               | :ref:`showfun`, :ref:`listfun`              |
++--------------------------------+---------------------------------------------+
+| :func:`registerType`           | :ref:`showfun`, :ref:`listfun`              |
++--------------------------------+---------------------------------------------+
+| :func:`require`                | every except :ref:`reducefun`               |
++--------------------------------+---------------------------------------------+
+| :func:`send`                   | :ref:`listfun`                              |
++--------------------------------+---------------------------------------------+
+| :func:`start`                  | :ref:`listfun`                              |
++--------------------------------+---------------------------------------------+
+| :func:`sum`                    | any                                         |
++--------------------------------+---------------------------------------------+
+| :func:`toJSON`                 | any                                         |
++--------------------------------+---------------------------------------------+
+
+Design functions context
+------------------------
+
+Each design function executes within special context of predefined objects,
+modules and functions:
+
+
+.. function:: emit(key, value)
+
+   Puts `key`-`value` pair into inner stack for further passing to CouchDB
+   when map function is done.
+
+   :param key: View's key.
+   :param value: Associated value with `key`.
+
+   .. code-block:: javascript
+
+      function(doc){
+        emit(doc._id, doc._rev);
+      }
+
+
+.. function:: getRow()
+
+   Extracts next row from the related view result.
+
+   :return: View result row
+   :rtype: object
+
+   .. code-block:: javascript
+
+      function(head, req){
+        send('[');
+        row = getRow();
+        if (row){
+          send(toJSON(row));
+          while(row = getRow()){
+            send(',');
+            send(toJSON(row));
+          }
+        }
+        return ']';
+      }
+
+
+.. data:: JSON
+
+   `JSON2 <https://git-wip-us.apache.org/repos/asf?p=couchdb.git;a=blob;f=share/server/json2.js>`_
+   object.
+
+
+.. function:: isArray(obj)
+
+   Helper to check is provided value `array` or not
+
+   :param obj: Any Javascript value
+   :return: ``true`` is `obj` is `array` typed, ``false`` otherwise.
+   :rtype: boolean
+
+
+.. function:: log(message)
+
+   :param message: Message to log in at CouchDB `INFO` level.
+
+   .. code-block:: javascript
+
+      function(doc){
+        log('Procesing doc ' + doc['_id']);
+        emit(doc['_id'], null);
+      }
+
+   On map function run in CouchDB logs (e.g. at `/var/log/couchdb/couch.log`)
+   you may find next record:
+
+   .. code-block:: text
+
+      [Sat, 03 Nov 2012 17:38:02 GMT] [info] [<0.7543.0>] OS Process #Port<0.3289> Log :: Processing doc 8d300b86622d67953d102165dbe99467
+
+
+.. function:: provides(key, func)
+
+   Registers callable handler for specified MIME key.
+
+   :param key: MIME key previously defined by :func:`registerType`
+   :param func: MIME type handler.
+
+
+.. function:: registerType(key, *mimes)
+
+   Registers list of MIME types by associated `key`.
+
+   :param key: MIME types
+   :param mimes: MIME types enumeration.
+
+   Predefined mappings (`key`-`array`):
+
+   - **all**: ``*/*``
+   - **text**: ``text/plain; charset=utf-8``, ``txt``
+   - **html**: ``text/html; charset=utf-8``
+   - **xhtml**: ``application/xhtml+xml``, ``xhtml``
+   - **xml**: ``application/xml``, ``text/xml``, ``application/x-xml``
+   - **js**: ``text/javascript``, ``application/javascript``,
+     ``application/x-javascript``
+   - **css**: ``text/css``
+   - **ics**: ``text/calendar``
+   - **csv**: ``text/csv``
+   - **rss**: ``application/rss+xml``
+   - **atom**: ``application/atom+xml``
+   - **yaml**: ``application/x-yaml``, ``text/yaml``
+   - **multipart_form**: ``multipart/form-data``
+   - **url_encoded_form**: ``application/x-www-form-urlencoded``
+   - **json**: ``application/json``, ``text/x-json``
+
+
+.. function:: require(path)
+
+   Loads CommonJS module by specified `path`. Path shouldn't starts with slash.
+
+   :param path: CommonJS module path started from design document root.
+   :return: Exported statements.
+
+
+.. function:: send(chunk)
+
+   Sends a single string `chunk` in response.
+
+   :param chunk: Text chunk
+
+   .. code-block:: javascript
+
+      function(head, req){
+        send('Hello,');
+        send(' ');
+        send('Couch');
+        return !
+      }
+
+
+.. function:: start(init_resp)
+
+   Initiates chunked response. As an option, custom
+   :ref:`response <response_object>` object may be sent at this point.
+   For `list`-functions only!
+
+   .. note::
+      Only at this point list functions may set response `HTTP code` and
+      `headers`. Also, you need to run this function before :func:`send`,
+      :func:`getRow` or `return` statement or query server will implicitly call
+      this function with empty object (``{}``).
+
+   .. code-block:: javascript
+
+      function(head, req){
+        start({
+          "code": 302,
+          "headers": {
+            "Location": "http://couchdb.apache.org"
+          }
+        });
+        return "Relax!";
+      }
+
+
+.. function:: sum(arr)
+
+   Summarize `arr` items.
+
+   :param arr: Array of numbers.
+   :rtype: number
+
+
+.. function:: toJSON(obj)
+
+   Encodes `obj` to JSON string. Actually is a proxy to ``JSON.stringify``
+   method.
+
+   :param obj: JSON encodable object.
+   :return: JSON string.
+
+
+
+CommonJS Modules
+----------------
+
+`CommonJS Modules <http://wiki.commonjs.org/wiki/Modules/1.1.1>`_ is the one of
+major CouchDB feature introduced in 0.11.0 version that allows to create modular
+design functions without needs to duplicate a lot of same functionality.
+
+Example of CommonJS module that checks user permissions:
+
+.. code-block:: javascript
+
+    function user_context(userctx, secobj){
+      var is_admin = function(){
+        return userctx.indexOf('_admin') != -1;
+      }
+      var is_db_admin = function(){
+        if (is_admin() || !secobj){
+          return true;
+        }
+        if (secobj.admins.names.indexOf(userctx.name) != -1){
+          return true;
+        }
+        for (var idx in userctx.roles){
+          if (secobj.admins.roles.indexOf(userctx.roles[idx]) != -1){
+            return true;
+          }
+        }
+        return false;
+      }
+      var is_db_member = function(){
+        if (is_admin() || is_db_admin() || !secobj){
+          return true;
+        }
+        if (secobj.members.names.indexOf(userctx.name) != -1){
+          return true;
+        }
+        for (var idx in userctx.roles){
+          if (secobj.members.roles.indexOf(userctx.roles[idx]) != -1){
+            return true;
+          }
+        }
+        return false;
+      }
+      var has_all_roles = function(roles){
+        for (var idx in roles){
+          if (userctx.roles.indexOf(roles[idx]) == -1){
+            return false;
+          }
+        }
+        return true;
+      }
+      var has_any_role = function(roles){
+        for (var idx in roles){
+          if (userctx.roles.indexOf(roles[idx]) != -1){
+            return true;
+          }
+        }
+        return false;
+      }
+      return {
+        'is_admin': is_admin,
+        'is_db_admin': is_db_admin,
+        'is_db_member': is_db_member,
+        'has_all_roles': has_all_roles,
+        'has_any_role': has_any_role
+      }
+    }
+
+    exports['user'] = user_context
+
+Each module has access to additional global variables:
+
+- **module** (`object`): Contains information about stored module.
+
+  - **id** (`string`): Module id that is actually JSON path in ddoc context.
+  - **current** (`code`): Compiled module code object.
+  - **parent** (`object`): Parent frame.
+  - **exports** (`object`): Export statements.
+
+- **exports** (`object`): Shortcut to ``module.exports`` object.
+
+Lets place module above within design document under `lib/validate` path.
+Now we could use it in our design functions:
+
+.. code-block:: javascript
+
+    function(newdoc, olddoc, userctx, secobj){
+      user = require('lib/validate').user(userctx, secobj);
+      if (user.is_admin()){
+        return true;
+      }
+      if (newdoc.author != olddoc.author){
+        throw({'forbidden': 'unable to update `author` field'});
+      }
+    }
+
+
+.. _queryserver_erlang:
+
+Erlang
+======
+
+.. warning::
+
+   Erlang query server runs out of sandbox feature like JavaScript has to!
+   This means, that Erlang code has full access to your OS, file system and
+   network which may leads to security issues. While Erlang functions are
+   faster than JavaScript ones, you need to be careful with running them,
+   especially if they  wasn't written by your own hands.
+
+   Keep in mind: don't trust every code - review it first before running.
+
+
+.. note::
+
+   Due to security restriction, Erlang query server is disabled by default.
+   To enable it you'll need to edit your `local.ini` to include a
+   ``native_query_servers`` section:
+
+   .. code-block:: ini
+
+      [native_query_servers]
+      erlang = {couch_native_process, start_link, []}
+
+   And don't forget to restart CouchDB after that and use ``language: "erlang"``
+   property in your Erlang design documents.
+
+
+.. function:: Emit(Id, Value)
+
+   Emits `key`-`value` pair to view indexer process.
+
+   .. code-block:: erlang
+
+      fun({Doc}) ->
+        <<K,_/binary>> = proplists:get_value(<<"_rev">>, Doc, null),
+        V = proplists:get_value(<<"_id">>, Doc, null),
+        Emit(<<K>>, V)
+      end.
+
+
+.. function:: FoldRows(Fun, Acc)
+
+   Helper to iterate over all rows in list function.
+
+   :param Fun: Function object.
+   :param Acc: Previous returned value by `Fun`.
+
+   .. code-block:: erlang
+
+      fun(Head, {Req}) ->
+        Fun = fun({Row}, Acc) ->
+          Id = couch_util:get_value(<<"id">>, Row),
+          Send(list_to_binary(io_lib:format("Previous doc id: ~p~n", [Acc]))),
+          Send(list_to_binary(io_lib:format("Current  doc id: ~p~n", [Id]))),
+          {ok, Id}
+        end,
+        FoldRows(Fun, nil),
+        ""
+      end.
+
+
+.. function:: GetRow()
+
+   Retrieves next row from related view result.
+
+   .. code-block:: erlang
+
+      %% FoldRows background implementation.
+      %% https://git-wip-us.apache.org/repos/asf?p=couchdb.git;a=blob;f=src/couchdb/couch_native_process.erl;hb=HEAD#l368
+      %%
+      foldrows(GetRow, ProcRow, Acc) ->
+        case GetRow() of
+          nil ->
+            {ok, Acc};
+          Row ->
+            case (catch ProcRow(Row, Acc)) of
+              {ok, Acc2} ->
+                foldrows(GetRow, ProcRow, Acc2);
+              {stop, Acc2} ->
+                {ok, Acc2}
+            end
+      end.
+
+.. function:: Log(Msg)
+
+   :param Msg: Message to log in at CouchDB `INFO` level.
+
+   .. code-block:: erlang
+
+      fun({Doc}) ->
+        <<K,_/binary>> = proplists:get_value(<<"_rev">>, Doc, null),
+        V = proplists:get_value(<<"_id">>, Doc, null),
+        Log(lists:flatten(io_lib:format("Hello from ~s doc!", [V]))),
+        Emit(<<K>>, V)
+      end.
+
+   On map function run in CouchDB logs (e.g. at `/var/log/couchdb/couch.log`)
+   you may find next record:
+
+   .. code-block:: text
+
+      [Sun, 04 Nov 2012 11:33:58 GMT] [info] [<0.9144.2>] Hello from 8d300b86622d67953d102165dbe99467 doc!
+
+
+.. function:: Send(Chunk)
+
+   Sends a single string `Chunk` in response.
+
+   .. code-block:: erlang
+
+      fun(Head, {Req}) ->
+        Send("Hello,"),
+        Send(" "),
+        Send("Couch"),
+        "!"
+      end.
+
+   Function above produces next response:
+
+   .. code-block:: text
+
+      Hello, Couch!
+
+
+.. function:: Start(Headers)
+
+   :param Headers: Proplist of :ref:`response object<response_object>`.
+
+   Initialize :ref:`listfun` response. At this point response code and headers
+   may be defined. For example, next function redirect to CouchDB web site:
+
+   .. code-block:: erlang
+
+      fun(Head, {Req}) ->
+        Start({[{<<"code">>, 302},
+                {<<"headers">>, {[
+                  {<<"Location">>, <<"http://couchdb.apache.org">>}]
+                }}
+              ]}),
+        "Relax!"
+      end.
+
+