1
0
Fork 0
arangodb/Doxygen/doc/UserManualActions.html

212 lines
19 KiB
HTML

<html><head><title>ArangoDB Manual</title> <style media="screen" type="text/css" style="display:none">body{background-color:white;font:13px Helvetica,arial,freesans,clean,sans-serif;line-height:1.4;color:#333;}#access{font-size:16px;margin-left:12px;display:block;margin-left:10px;margin-right:10px;background-color:#F3F1EE!important;}#access a{border-right:1px solid #DBDEDF;color:#A49F96;display:block;line-height:38px;padding:0 10px;text-decoration:none;}#navigation ul{text-transform:uppercase;list-style:none;margin:0;}#navigation li{float:left;position:relative;}#container{width:920px;margin:0 auto;}a{color:#4183C4;text-decoration:none;}.contents h2{font-size:24px;border-bottom:1px solid #CCC;color:black;}.contents h1{font-size:33px;border-bottom:1px solid #CCC;color:black;}.clearfix:after{content:".";display:block;clear:both;font-size:0;height:0;visibility:hidden;}/**/ *:first-child+html .clearfix{min-height:0;}/**/ * html .clearfix{height:1%;}</style></head><body><div id="container"><img src="images/logo_arangodb.png" width="397" height="67" alt="ArangoDB"><div id="access" role="navigation"><div id="navigation"><ul id="menu-ahome" class="menu"><li><a href="Home.html">Table of contents</a></li> <li><a href="http://www.arangodb.org">ArangoDB homepage</a></li></ul></div><div class="clearfix"></div></div><div>
<!-- Generated by Doxygen 1.7.3 -->
</div>
<div class="header">
<div class="headertitle">
<h1>Arango Actions </h1> </div>
</div>
<div class="contents">
<div class="textblock"><p>Please note, that user Actions in ArangoDB are still preliminary and details are subject to change.</p>
<hr/>
<ul>
<li>
<a class="el" href="UserManualActions.html">Arango Actions</a> <ul>
<li>
<a class="el" href="UserManualActions.html#UserManualActionsIntro">Introduction to User Actions</a> </li>
<li>
<a class="el" href="UserManualActions.html#UserManualActionsHelloWorld">A Hello World Example</a> </li>
<li>
<a class="el" href="UserManualActions.html#UserManualActionsHelloJson">A Hello World Example for JSON</a> </li>
<li>
<a class="el" href="UserManualActions.html#UserManualActionsEcho">A Dynamic Example</a> </li>
<li>
<a class="el" href="UserManualActions.html#UserManualActionsDYO">Define Your Own Callback</a> </li>
<li>
<a class="el" href="UserManualActions.html#UserManualActionsAdvanced">Advanced Usages</a> </li>
</ul>
</li>
</ul>
<hr/>
<h2><a class="anchor" id="UserManualActionsIntro"></a>
Introduction to User Actions</h2>
<p>In some ways the communication layer of the ArangoDB server behaves like a Web server. Unlike a Web server, it normally responses to HTTP requests by delivering JSON objects. Remember, documents in the database are just JSON objects. So, most of the time the HTTP response will contain a JSON document from the database as body. You can extract the documents stored in the database using HTTP <code>GET</code>. You can store documents using HTTP <code>POST</code>.</p>
<p>However, there is something more. You can write small sniplets - so called actions - to extend the database. The idea of actions is that sometimes it is better to store parts of the business logic within AnrangoDB.</p>
<p>The simplest example is the age of a person. Assume you store information about people in your database. It is an anti-pattern to store the age, because it changes every now and then. Therefore, you normally store the birthday and let the client decide what to do with it. However, if you have many different clients, it might be easier to enrich the person document with the age using actions once on the server side.</p>
<p>Or, for instance, if you want to apply some statistics to large data-sets and you cannot easily express this as query. You can define a action instead of transferring the whole data to the client and do the computation on the client.</p>
<p>Actions are also useful if you want to restrict and filter data according to some complex permission system.</p>
<p>The ArangoDB server can deliver all kinds of information, JSON being only one possible format. You can also generate HTML or images. However, a Web server is normally better suited for the task as it also implements various caching strategies, language selection, compression and so on. Having said that, there are still situations where it might be suitable to use the ArangoDB to deliver HTML pages - static or dynamic. An simple example is the built-in administration interface. You can access it using any modern browser and there is no need for a separate Apache or IIS.</p>
<p>The following sections will explain actions within ArangoDB and show how to define them. The examples start with delivering static HTML pages - even if this is not the primary use-case for actions. The later sections will then show you, how to code some pieces of your business logic and return JSON objects.</p>
<p>The interface is loosely modelled after the JavaScript classes for HTTP request and responses found in node.js and the middleware/routing aspects of connect.js and express.js.</p>
<p>Note that unlike node.js, ArangoDB is multi-threaded and there is no easy way to share state between queries inside the JavaScript engine. If such state information is required, you need to use the database itself.</p>
<h2><a class="anchor" id="UserManualActionsHelloWorld"></a>
A Hello World Example</h2>
<p>The client API or browser sends a HTTP request to the ArangoDB server and the server returns a HTTP response to the client. A HTTP requests consists of a method, normally <code>GET</code> or <code>POST</code> when using a browser, and a request path like <code>/hello/world</code>. For a real Web server there are a zillion of other thing to consider, we will ignore this for the moment. The HTTP response contains a content type, describing how to interpret the returned data, and the data itself.</p>
<p>In the following example, we want to define action in ArangoDB, so that the server returns the HTML document</p>
<div class="fragment"><pre class="fragment"> &lt;html&gt;
&lt;body&gt;
Hello World
&lt;/body&gt;
&lt;/html&gt;
</pre></div><p>if asked <code>GET /hello/world</code>.</p>
<p>The server needs to know what function to call or what document to deliver if it receives a request. This is called routing. All the routing information of ArangoDB is stored in a collection <code>_routing</code>. Each entry in this collections describes how to deal with a particular request path.</p>
<p>For the above example, add the following document to the_routing} collection:</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; db._routing.save({
........&gt; path: <span class="stringliteral">&quot;/hello/world&quot;</span>,
........&gt; callback: {
........&gt; contentType: <span class="stringliteral">&quot;text/html&quot;</span>,
........&gt; body: <span class="stringliteral">&quot;&lt;html&gt;&lt;body&gt;Hello World&lt;/body&gt;&lt;/html&gt;&quot;</span> }});
</pre></div><p>In order to activate the new routing, you must either restart the server or call the internal reload function.</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; require(<span class="stringliteral">&quot;internal&quot;</span>).reloadRouting()
</pre></div><p>Now use the browser and access</p>
<p><code><a href="http://localhost:8529/hello/world">http://localhost:8529/hello/world</a></code></p>
<h2><a class="anchor" id="UserManualActionsHelloJson"></a>
A Hello World Example for JSON</h2>
<p>If you change the example slightly, then a JSON object will be delivered.</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; db._routing.save({
........&gt; path: <span class="stringliteral">&quot;/hello/json&quot;</span>,
........&gt; callback: {
........&gt; contentType: <span class="stringliteral">&quot;application/json&quot;</span>,
........&gt; body: <span class="stringliteral">&quot;{ \&quot;hello\&quot; : \&quot;world\&quot; }&quot;</span> }});
arangosh&gt; require(<span class="stringliteral">&quot;internal&quot;</span>).reloadRouting()
</pre></div><p>Again check with your browser</p>
<p><code><a href="http://localhost:8529/hello/json">http://localhost:8529/hello/json</a></code></p>
<p>Depending on your browser and installed add-ons you will either see the JSON object or a download dialog. If your browser wants to open an external application to display the JSON object, you can change the <code>contentType</code> to <code>"text/plain"</code> for the example. This makes it easier to check the example using a browser. Or use <code>curl</code> to access the server.</p>
<div class="fragment"><pre class="fragment"> bash&gt; curl <span class="stringliteral">&quot;http://127.0.0.1:8529/hello/json&quot;</span> &amp;&amp; echo
{ <span class="stringliteral">&quot;hello&quot;</span> : <span class="stringliteral">&quot;world&quot;</span> }
</pre></div><h2><a class="anchor" id="UserManualActionsEcho"></a>
A Dynamic Example</h2>
<p>The above examples deliver static content, which is fine for an example. But the real power of actions lies in dynamic actions which use JavaScript to construct the result.</p>
<p>A very simple example is the function <code>echoRequest</code> defined in the module <code>org/arangodb/actions</code>.</p>
<div class="fragment"><pre class="fragment"> function (req, res, next, options) {
var result;
result = { request: req, options: options };
res.responseCode = exports.HTTP_OK;
res.contentType = <span class="stringliteral">&quot;application/json&quot;</span>;
res.body = JSON.stringify(result);
}
</pre></div><p>That functions accepts a request and returns this request as JSON object.</p>
<p>It is not possible to store functions directly in the routing table, but you can call functions defined in modules. In the above example the function can be accessed from JavaScript as:</p>
<p><code>require("org/arangodb/actions").echoRequest</code></p>
<p>You can use it in the routing collection by specifying the name <code>"org/arangodb/actions/echoRequest"</code>.</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; db._routing.save({
........&gt; path: <span class="stringliteral">&quot;/hello/echo&quot;</span>,
........&gt; callback: <span class="stringliteral">&quot;org/arangodb/actions/echoRequest&quot;</span> });
</pre></div><p>Reload the routing and check</p>
<p><code><a href="http://127.0.0.1:8529/hello/echo">http://127.0.0.1:8529/hello/echo</a></code></p>
<p>You should see something like</p>
<div class="fragment"><pre class="fragment"> {
<span class="stringliteral">&quot;request&quot;</span>: {
<span class="stringliteral">&quot;prefix&quot;</span>: <span class="stringliteral">&quot;/hello/echo&quot;</span>,
<span class="stringliteral">&quot;suffix&quot;</span>: [
<span class="stringliteral">&quot;hello&quot;</span>,
<span class="stringliteral">&quot;echo&quot;</span>
],
<span class="stringliteral">&quot;path&quot;</span>: <span class="stringliteral">&quot;/hello/echo&quot;</span>,
<span class="stringliteral">&quot;headers&quot;</span>: {
<span class="stringliteral">&quot;accept-encoding&quot;</span>: <span class="stringliteral">&quot;gzip, deflate&quot;</span>,
<span class="stringliteral">&quot;accept-language&quot;</span>: <span class="stringliteral">&quot;de-de,de;q=0.8,en-us;q=0.5,en;q=0.3&quot;</span>,
<span class="stringliteral">&quot;connection&quot;</span>: <span class="stringliteral">&quot;keep-alive&quot;</span>,
<span class="stringliteral">&quot;content-length&quot;</span>: <span class="stringliteral">&quot;0&quot;</span>,
<span class="stringliteral">&quot;host&quot;</span>: <span class="stringliteral">&quot;localhost:8529&quot;</span>,
<span class="stringliteral">&quot;user-agent&quot;</span>: <span class="stringliteral">&quot;Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0&quot;</span>
},
<span class="stringliteral">&quot;requestType&quot;</span>: <span class="stringliteral">&quot;GET&quot;</span>,
<span class="stringliteral">&quot;parameters&quot;</span>: { }
},
<span class="stringliteral">&quot;options&quot;</span>: { }
}
</pre></div><p>Please note that</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; db._routing.save({
........&gt; path: <span class="stringliteral">&quot;/hello/echo&quot;</span>,
........&gt; callback: <span class="stringliteral">&quot;org/arangodb/actions/echoRequest&quot;</span> });
</pre></div><p>is a short-cut for</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; db._routing.save({
........&gt; path: <span class="stringliteral">&quot;/hello/echo-long&quot;</span>,
........&gt; callback: {
........&gt; <span class="keywordflow">for</span>: <span class="stringliteral">&quot;org/arangodb/actions&quot;</span>,
........&gt; <span class="keywordflow">do</span>: <span class="stringliteral">&quot;echoRequest&quot;</span> }});
</pre></div><p>The verbose form allows you to pass options to the called function:</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; a = db._routing.firstExample({path: <span class="stringliteral">&quot;/hello/echo-long&quot;</span>});
arangosh&gt; a.callback.options = { option: <span class="stringliteral">&quot;my option1&quot;</span> };
arangosh&gt; db._replace(a, a);
</pre></div><p>You should now see the options in the result.</p>
<div class="fragment"><pre class="fragment"> {
<span class="stringliteral">&quot;request&quot;</span>: {
<span class="stringliteral">&quot;prefix&quot;</span>: <span class="stringliteral">&quot;/hello/echo-long&quot;</span>,
...
},
<span class="stringliteral">&quot;options&quot;</span>: {
<span class="stringliteral">&quot;option&quot;</span>: <span class="stringliteral">&quot;my option1&quot;</span>
}
}
</pre></div><h2><a class="anchor" id="UserManualActionsDYO"></a>
Define Your Own Callback</h2>
<p>You can define your own callbacks by adding a new module to ArangoDB. In order to avoid name clashes modules should be named</p>
<p><code>tld/domain/modulename</code></p>
<p>where <code>domain.tld</code> is your domain name. For development you can store your code in files in the filesystem, see MODULES_PATH and MODULES.</p>
<p>However, when you are finished with the development, you can rollout the module code by storing it inside the <code>_modules</code> collection.</p>
<p>Create a file <code>hello-world.js</code> with the following content:</p>
<div class="fragment"><pre class="fragment"> var actions = require(<span class="stringliteral">&quot;org/arangodb/actions&quot;</span>);
exports.helloWorld = function (req, res) {
res.contentType = <span class="stringliteral">&quot;text/html&quot;</span>;
res.responseCode = actions.HTTP_OK;
res.body = <span class="stringliteral">&quot;&lt;html&gt;&lt;body&gt;Hello World!&lt;/body&gt;&lt;/html&gt;&quot;</span>;
};
</pre></div><p>Load this file as new module <code>de/celler/hello-world</code> into the database</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; require(<span class="stringliteral">&quot;internal&quot;</span>).defineModule(<span class="stringliteral">&quot;de/celler/hello-world&quot;</span>, <span class="stringliteral">&quot;hello-world.js&quot;</span>);
</pre></div><p>Define a corresponding routing</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; db._routing.save({
........&gt; path: <span class="stringliteral">&quot;/my/echo&quot;</span>,
........&gt; callback: <span class="stringliteral">&quot;de/celler/hello-world/helloWorld&quot;</span> });
arangosh&gt; require(<span class="stringliteral">&quot;internal&quot;</span>).reloadRouting()
</pre></div><p>and check it</p>
<p><code><a href="http://localhost:8529/my/echo">http://localhost:8529/my/echo</a></code></p>
<h2><a class="anchor" id="UserManualActionsAdvanced"></a>
Advanced Usages</h2>
<p>For detailed information see the reference manual.</p>
<h3><a class="anchor" id="UserManualActionsAdvancedPrefix"></a>
Using Prefixes</h3>
<p>All the above definitions require an exact match. If you set the <code>prefix</code> attribute to <code>true</code>, additional paths are ignored and the URL also results in a match.</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; db._routing.save({
........&gt; path: <span class="stringliteral">&quot;/hello&quot;</span>,
........&gt; prefix: <span class="keyword">true</span>,
........&gt; callback: <span class="stringliteral">&quot;org/arangodb/actions/echoRequest&quot;</span> });
</pre></div><p>Now try</p>
<p><code><a href="http://localhost:8529/hello/this/is/ignored/but/available/in/path">http://localhost:8529/hello/this/is/ignored/but/available/in/path</a></code></p>
<p>The complete path is available in the <code>path</code> attribute, while the matched prefix is available in the <code>prefix</code> attribute.</p>
<div class="fragment"><pre class="fragment"> {
<span class="stringliteral">&quot;request&quot;</span>: {
<span class="stringliteral">&quot;prefix&quot;</span>: <span class="stringliteral">&quot;/hello&quot;</span>,
<span class="stringliteral">&quot;path&quot;</span>: <span class="stringliteral">&quot;/hello/this/is/ignored/but/available/in/path&quot;</span>,
...
},
<span class="stringliteral">&quot;options&quot;</span>: { }
}
</pre></div><h3><a class="anchor" id="UserManualActionsAdvancedMiddleware"></a>
Writing Middleware</h3>
<p>Assume, you want to log every request. In this case you can easily define an action for the whole url-space <code>/</code>. This action simply logs the requests, calls the next in line, and logs the response.</p>
<div class="fragment"><pre class="fragment"> exports.logRequest = function (req, res, next, options) {
console.log(<span class="stringliteral">&quot;received request: %s&quot;</span>, JSON.stringify(req));
next();
console.log(<span class="stringliteral">&quot;produced response: %s&quot;</span>, JSON.stringify(res));
};
</pre></div><p>This functions is available as <code>org/arangodb/actions/logRequest</code>. You need to tell ArangoDB that it is should use a prefix match and that the shortest match should win in this case:</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; db._routing.save({
........&gt; path: <span class="stringliteral">&quot;/&quot;</span>,
........&gt; topdown: <span class="keyword">true</span>,
........&gt; prefix: <span class="keyword">true</span>,
........&gt; callback: <span class="stringliteral">&quot;org/arangodb/actions/logRequest&quot;</span> });
</pre></div><h3><a class="anchor" id="UserManualActionsAdvancedeRedirect"></a>
Redirects</h3>
<p>Use the following for a permanent redirect:</p>
<div class="fragment"><pre class="fragment"> arangosh&gt; db._routing.save({
........&gt; path: <span class="stringliteral">&quot;/&quot;</span>,
........&gt; topdown: <span class="keyword">true</span>,
........&gt; prefix: <span class="keyword">true</span>,
........&gt; callback: { redirect: <span class="stringliteral">&quot;http://somewhere.else.org/hallo&quot;</span> });
</pre></div> </div></div>
</div></body></html>