mirror of https://gitee.com/bigwinds/arangodb
Work on the documentation to contain real examples.
This commit is contained in:
parent
3c988dc7f1
commit
3bb3d6b623
|
@ -19,25 +19,17 @@ about the optimizer's view of the query.
|
|||
|
||||
Here's an example that shows the execution plan for a simple query, using the `explain`
|
||||
method of `ArangoStatement`:
|
||||
```
|
||||
arangosh> query = "FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value";
|
||||
arangosh> db._create("test");
|
||||
arangosh> for (i = 0; i < 100; ++i) { db.test.save({ value: i }); }
|
||||
arangosh> db.test.ensureSkiplist("value");
|
||||
arangosh> stmt = db._createStatement(query);
|
||||
arangosh> stmt.explain();
|
||||
{
|
||||
"plan" : {
|
||||
...
|
||||
},
|
||||
"warnings" : [
|
||||
...
|
||||
],
|
||||
"stats" : {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@startDocuBlockInline AQLEXP_01_explainCreate
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_01_explainCreate}
|
||||
~addIgnoreCollection("test")
|
||||
db._create("test");
|
||||
for (i = 0; i < 100; ++i) { db.test.save({ value: i }); }
|
||||
db.test.ensureSkiplist("value");
|
||||
stmt = db._createStatement("FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value");
|
||||
stmt.explain();
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_01_explainCreate
|
||||
|
||||
The result details will be very verbose so they are not shown here in full. Instead,
|
||||
let's take a closer look at the results step by step.
|
||||
|
@ -49,17 +41,13 @@ Each processing step is carried out by a so-called *execution node*
|
|||
|
||||
The `nodes` attribute of the `explain` result contains these *execution nodes* in
|
||||
the *execution plan*. The output is still very verbose, so here's a shorted form of it:
|
||||
```
|
||||
arangosh> stmt.explain().plan.nodes.map(function (node) { return node.type; });
|
||||
[
|
||||
"SingletonNode",
|
||||
"IndexRangeNode",
|
||||
"CalculationNode",
|
||||
"FilterNode",
|
||||
"CalculationNode",
|
||||
"ReturnNode"
|
||||
]
|
||||
```
|
||||
|
||||
@startDocuBlockInline AQLEXP_02_explainOverview
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_02_explainOverview}
|
||||
~var stmt = db._createStatement("FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value");
|
||||
stmt.explain().plan.nodes.map(function (node) { return node.type; });
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_02_explainOverview
|
||||
|
||||
*Note that the list of nodes might slightly change in future versions of ArangoDB if
|
||||
new execution node types get added or the optimizer will create somewhat more
|
||||
|
@ -108,17 +96,12 @@ Additionally, the optimizer has done more work to generate an execution plan tha
|
|||
avoids as much expensive operations as possible. Here is the list of optimizer rules
|
||||
that were applied to the plan:
|
||||
|
||||
arangosh> stmt.explain().plan.rules;
|
||||
[
|
||||
"move-calculations-up",
|
||||
"move-filters-up",
|
||||
"remove-redundant-calculations",
|
||||
"remove-unnecessary-calculations",
|
||||
"move-calculations-up-2",
|
||||
"move-filters-up-2",
|
||||
"use-index-range",
|
||||
"use-index-for-sort"
|
||||
]
|
||||
@startDocuBlockInline AQLEXP_03_explainRules
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_03_explainRules}
|
||||
~var stmt = db._createStatement("FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value");
|
||||
stmt.explain().plan.rules;
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_03_explainRules
|
||||
|
||||
Here is the meaning of these rules in context of this query:
|
||||
* `move-calculations-up`: moves a *CalculationNode* as far up in the processing pipeline
|
||||
|
@ -148,15 +131,12 @@ in the optimizer pipeline.
|
|||
The list of collections used in a plan (and query) is contained in the `collections`
|
||||
attribute of a plan:
|
||||
|
||||
```
|
||||
arangosh> stmt.explain().plan.collections
|
||||
[
|
||||
{
|
||||
"name" : "test",
|
||||
"type" : "read"
|
||||
}
|
||||
]
|
||||
```
|
||||
@startDocuBlockInline AQLEXP_04_explainCollections
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_04_explainCollections}
|
||||
~var stmt = db._createStatement("FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value");
|
||||
stmt.explain().plan.collections
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_04_explainCollections
|
||||
|
||||
The `name` attribute contains the name of the `collection`, and `type` is the
|
||||
access type, which can be either `read` or `write`.
|
||||
|
@ -186,17 +166,12 @@ generated, set the option `allPlans` to `true`:
|
|||
This will return a list of all plans in the `plans` attribute instead of in the
|
||||
`plan` attribute:
|
||||
|
||||
```
|
||||
arangosh> stmt.explain({ allPlans: true });
|
||||
{
|
||||
"plans" : [
|
||||
...
|
||||
],
|
||||
"warnings" : [
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
@startDocuBlockInline AQLEXP_05_explainAllPlans
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_05_explainAllPlans}
|
||||
~var stmt = db._createStatement("FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value");
|
||||
stmt.explain({ allPlans: true });
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_05_explainAllPlans
|
||||
|
||||
!SUBSECTION Retrieving the plan as it was generated by the parser / lexer
|
||||
|
||||
|
@ -206,15 +181,13 @@ the explain on a cluster coordinator) set the option `rules` to `-all`:
|
|||
|
||||
This will return an unoptimized plan in the `plan`:
|
||||
|
||||
```
|
||||
arangosh> stmt.explain({ optimizer: { rules: [ "-all" ] } });
|
||||
{
|
||||
"plan" : {
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
@startDocuBlockInline AQLEXP_06_explainUnoptimizedPlans
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_06_explainUnoptimizedPlans}
|
||||
~var stmt = db._createStatement("FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value");
|
||||
stmt.explain({ optimizer: { rules: [ "-all" ] } });
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_06_explainUnoptimizedPlans
|
||||
|
||||
Note that some optimisations are already done at parse time (i.e. evaluate simple constant
|
||||
calculation as `1 + 1`)
|
||||
|
||||
|
@ -229,32 +202,32 @@ pseudo-rule `all` matches all rules.
|
|||
Rules specified in `rules` are evaluated from left to right, so the following works to
|
||||
turn on just the one specific rule:
|
||||
|
||||
```
|
||||
arangosh> stmt.explain({ optimizer: { rules: [ "-all", "+use-index-range" ] } });
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
@startDocuBlockInline AQLEXP_07_explainSingleRulePlans
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_07_explainSingleRulePlans}
|
||||
~var stmt = db._createStatement("FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value");
|
||||
stmt.explain({ optimizer: { rules: [ "-all", "+use-index-range" ] } });
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_07_explainSingleRulePlans
|
||||
|
||||
By default, all rules are turned on. To turn off just a few specific rules, use something
|
||||
like this:
|
||||
|
||||
```
|
||||
arangosh> stmt.explain({ optimizer: { rules: [ "-use-index-range", "-use-index-for-sort" ] } });
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
@startDocuBlockInline AQLEXP_08_explainDisableSingleRulePlans
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_08_explainDisableSingleRulePlans}
|
||||
~var stmt = db._createStatement("FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value");
|
||||
stmt.explain({ optimizer: { rules: [ "-use-index-range", "-use-index-for-sort" ] } });
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_08_explainDisableSingleRulePlans
|
||||
|
||||
The maximum number of plans created by the optimizer can also be limited using the
|
||||
`maxNumberOfPlans` attribute:
|
||||
|
||||
```
|
||||
arangosh> stmt.explain({ maxNumberOfPlans: 1 });
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
@startDocuBlockInline AQLEXP_09_explainMaxNumberOfPlans
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_09_explainMaxNumberOfPlans}
|
||||
~var stmt = db._createStatement("FOR i IN test FILTER i.value > 97 SORT i.value RETURN i.value");
|
||||
stmt.explain({ maxNumberOfPlans: 1 });
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_09_explainMaxNumberOfPlans
|
||||
|
||||
!SUBSECTION Optimizer statistics
|
||||
|
||||
|
@ -273,16 +246,14 @@ The following attributes will be returned in the `stats` attribute of an `explai
|
|||
For some queries, the optimizer may produce warnings. These will be returned in
|
||||
the `warnings` attribute of the `explain` result:
|
||||
|
||||
```
|
||||
arangosh> stmt = db._createStatement("FOR i IN 1..10 RETURN 1 / 0")
|
||||
arangosh> stmt.explain().warnings;
|
||||
[
|
||||
{
|
||||
"code" : 1562,
|
||||
"message" : "division by zero"
|
||||
}
|
||||
]
|
||||
```
|
||||
@startDocuBlockInline AQLEXP_10_explainWarn
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AQLEXP_10_explainWarn}
|
||||
var stmt = db._createStatement("FOR i IN 1..10 RETURN 1 / 0")
|
||||
stmt.explain().warnings;
|
||||
~db._drop("test")
|
||||
~removeIgnoreCollection("test")
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock AQLEXP_10_explainWarn
|
||||
|
||||
There is an upper bound on the number of warning a query may produce. If that
|
||||
bound is reached, no further warnings will be returned.
|
||||
|
|
|
@ -28,28 +28,43 @@ describes how to deal with a particular request path.
|
|||
|
||||
For the above example, add the following document to the _routing collection:
|
||||
|
||||
```js
|
||||
arangosh> db._routing.save({
|
||||
url: {
|
||||
match: "/hello/world"
|
||||
},
|
||||
content: {
|
||||
contentType: "text/html",
|
||||
body: "<html><body>Hello World</body></html>"
|
||||
}
|
||||
});
|
||||
```
|
||||
@startDocuBlockInline HTML_01_routingCreateHtml
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{HTML_01_routingCreateHtml}
|
||||
|db._routing.save({
|
||||
| url: {
|
||||
| match: "/hello/world"
|
||||
| },
|
||||
| content: {
|
||||
| contentType: "text/html",
|
||||
| body: "<html><body>Hello World</body></html>"
|
||||
| }
|
||||
});
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock HTML_01_routingCreateHtml
|
||||
|
||||
In order to activate the new routing, you must either restart the server or call
|
||||
the internal reload function.
|
||||
|
||||
```js
|
||||
arangosh> require("internal").reloadRouting()
|
||||
```
|
||||
@startDocuBlockInline HTML_02_routingReload
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{HTML_02_routingReload}
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock HTML_02_routingReload
|
||||
|
||||
Now use the browser and access http:// localhost:8529/hello/world
|
||||
|
||||
You should see the *Hello World* in our browser.
|
||||
You should see the *Hello World* in our browser:
|
||||
|
||||
@startDocuBlockInline HTML_03_routingCurlHtml
|
||||
@EXAMPLE_ARANGOSH_RUN{HTML_03_routingCurlHtml}
|
||||
var url = "/hello/world";
|
||||
var response = logCurlRequest('GET', url);
|
||||
assert(response.code === 200);
|
||||
logRawResponse(response);
|
||||
db._query("FOR route IN _routing FILTER route.url.match == '/hello/world' REMOVE route in _routing")
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock HTML_03_routingCurlHtml
|
||||
|
||||
|
||||
!SECTION Matching a URL
|
||||
|
||||
|
@ -251,4 +266,4 @@ to test the above examples.
|
|||
{ url: { match: "/:something/world" }, content: "route 3" },
|
||||
{ url: { match: "/hello/*" }, content: "route 4" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,20 @@
|
|||
|
||||
If you change the example slightly, then a JSON object will be delivered.
|
||||
|
||||
@startDocuBlockInline routingCreateHelloWorld
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{routingCreateHelloWorld}
|
||||
@startDocuBlockInline JSON_01_routingCreateJsonHelloWorld
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_01_routingCreateJsonHelloWorld}
|
||||
|db._routing.save({
|
||||
| url: "/hello/json",
|
||||
| content: {
|
||||
| contentType: "application/json",
|
||||
| body: "{ \"hello\" : \"world\" }"
|
||||
| body: "{ 'hello' : 'world'}"
|
||||
| }
|
||||
});
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock routingCreateHelloWorld
|
||||
@endDocuBlock JSON_01_routingCreateJsonHelloWorld
|
||||
|
||||
Again check with your browser http:// localhost:8529/hello/json
|
||||
Again check with your browser or cURL http://localhost:8529/hello/json
|
||||
|
||||
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
|
||||
|
@ -23,15 +23,21 @@ application to display the JSON object, you can change the *contentType* to
|
|||
*"text/plain"* for the example. This makes it easier to check the example using
|
||||
a browser. Or use *curl* to access the server.
|
||||
|
||||
@startDocuBlockInline routingCurlHelloWorld
|
||||
@EXAMPLE_ARANGOSH_RUN{routingCurlHelloWorld}
|
||||
@startDocuBlockInline JSON_02_routingCurlJsonHelloWorld
|
||||
@EXAMPLE_ARANGOSH_RUN{JSON_02_routingCurlJsonHelloWorld}
|
||||
var url = "/hello/json";
|
||||
var response = logCurlRequest('GET', url);
|
||||
assert(response.code === 200);
|
||||
logJsonResponse(response);
|
||||
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock routingCurlHelloWorld
|
||||
@endDocuBlock JSON_02_routingCurlJsonHelloWorld
|
||||
|
||||
@startDocuBlockInline JSON_03_routingCleanupJsonHelloWorld
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_03_routingCleanupJsonHelloWorld}
|
||||
~db._query("FOR route IN _routing FILTER route.url == '/hello/json' REMOVE route in _routing")
|
||||
~require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock JSON_03_routingCleanupJsonHelloWorld
|
||||
|
||||
!SECTION Delivering Content
|
||||
|
||||
|
@ -43,14 +49,34 @@ starts when delivering dynamic content.
|
|||
|
||||
You can specify a body and a content-type.
|
||||
|
||||
```js
|
||||
{
|
||||
content: {
|
||||
contentType: "text/html",
|
||||
body: "<html><body>Hello World</body></html>"
|
||||
}
|
||||
}
|
||||
```
|
||||
@startDocuBlockInline JSON_05a_routingCreateContentTypeHelloWorld
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_05a_routingCreateContentTypeHelloWorld}
|
||||
|db._routing.save({
|
||||
| url: "/hello/contentType",
|
||||
| content: {
|
||||
| contentType: "text/html",
|
||||
| body: "<html><body>Hello World</body></html>"
|
||||
| }
|
||||
});
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock JSON_05a_routingCreateContentTypeHelloWorld
|
||||
|
||||
@startDocuBlockInline JSON_05b_routingCurlContentTypeHelloWorld
|
||||
@EXAMPLE_ARANGOSH_RUN{JSON_05b_routingCurlContentTypeHelloWorld}
|
||||
var url = "/hello/contentType";
|
||||
var response = logCurlRequest('GET', url);
|
||||
assert(response.code === 200);
|
||||
logRawResponse(response);
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock JSON_05b_routingCurlContentTypeHelloWorld
|
||||
|
||||
@startDocuBlockInline JSON_05c_routingCleanupContentTypeHelloWorld
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_05c_routingCleanupContentTypeHelloWorld}
|
||||
~db._query("FOR route IN _routing FILTER route.url == '/hello/contentType' REMOVE route in _routing")
|
||||
~require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock JSON_05c_routingCleanupContentTypeHelloWorld
|
||||
|
||||
If the content type is *text/plain* then you can use the short-cut
|
||||
|
||||
|
@ -90,8 +116,8 @@ function (req, res, options, next)
|
|||
|
||||
*Examples*
|
||||
|
||||
@startDocuBlockInline routingCreateHelloEcho
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{routingCreateHelloEcho}
|
||||
@startDocuBlockInline JSON_06_routingCreateHelloEcho
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_06_routingCreateHelloEcho}
|
||||
|db._routing.save({
|
||||
| url: "/hello/echo",
|
||||
| action: {
|
||||
|
@ -100,17 +126,24 @@ function (req, res, options, next)
|
|||
});
|
||||
~require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock routingCreateHelloEcho
|
||||
@endDocuBlock JSON_06_routingCreateHelloEcho
|
||||
|
||||
Reload the routing and check http:// 127.0.0.1:8529/hello/echo
|
||||
|
||||
You should see something like
|
||||
|
||||
@startDocuBlockInline fetchroutingCreateHelloEcho
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{fetchroutingCreateHelloEcho}
|
||||
arango.GET("hello/echo")
|
||||
@startDocuBlockInline JSON_07_fetchroutingCreateHelloEcho
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_07_fetchroutingCreateHelloEcho}
|
||||
arango.GET("/hello/echo")
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock fetchroutingCreateHelloEcho
|
||||
@endDocuBlock JSON_07_fetchroutingCreateHelloEcho
|
||||
|
||||
@startDocuBlockInline JSON_08_routingCleanupHelloEcho
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_08_routingCleanupHelloEcho}
|
||||
~db._query("FOR route IN _routing FILTER route.url == '/hello/echo' REMOVE route in _routing")
|
||||
~require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock JSON_08_routingCleanupHelloEcho
|
||||
|
||||
The request might contain *path*, *prefix*, *suffix*, and *urlParameters*
|
||||
attributes. *path* is the complete path as supplied by the user and always
|
||||
|
@ -148,8 +181,8 @@ called.
|
|||
|
||||
*Examples*
|
||||
|
||||
@startDocuBlockInline routingCreateEchoController
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{routingCreateEchoController}
|
||||
@startDocuBlockInline JSON_09_routingCreateEchoController
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_09_routingCreateEchoController}
|
||||
|db._routing.save({
|
||||
| url: "/hello/echo",
|
||||
| action: {
|
||||
|
@ -158,7 +191,23 @@ called.
|
|||
});
|
||||
~require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock routingCreateEchoController
|
||||
@endDocuBlock JSON_09_routingCreateEchoController
|
||||
|
||||
Reload the routing and check http:// 127.0.0.1:8529/hello/echo:
|
||||
|
||||
@startDocuBlockInline JSON_10_fetchroutingCreateEchoController
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_10_fetchroutingCreateEchoController}
|
||||
arango.GET("/hello/echo")
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock JSON_10_fetchroutingCreateEchoController
|
||||
|
||||
@startDocuBlockInline JSON_11_routingCleanupEchoController
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_11_routingCleanupEchoController}
|
||||
~db._query("FOR route IN _routing FILTER route.url == '/hello/echo' REMOVE route in _routing")
|
||||
~require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock JSON_11_routingCleanupEchoController
|
||||
|
||||
|
||||
!SUBSECTION Prefix Action Controller
|
||||
|
||||
|
@ -206,8 +255,8 @@ You can also store a function directly in the routing table.
|
|||
|
||||
*Examples*
|
||||
|
||||
@startDocuBlockInline routingCreateEchoFunction
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{routingCreateEchoFunction}
|
||||
@startDocuBlockInline JSON_12a_routingCreateEchoFunction
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_12a_routingCreateEchoFunction}
|
||||
|db._routing.save({
|
||||
| url: "/hello/echo",
|
||||
| action: {
|
||||
|
@ -216,7 +265,15 @@ You can also store a function directly in the routing table.
|
|||
});
|
||||
~require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock routingCreateEchoFunction
|
||||
@endDocuBlock JSON_12a_routingCreateEchoFunction
|
||||
|
||||
@startDocuBlockInline JSON_12b_fetchroutingEchoFunction
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_12b_fetchroutingEchoFunction}
|
||||
arango.GET("hello/echo")
|
||||
db._query("FOR route IN _routing FILTER route.url == '/hello/echo' REMOVE route in _routing")
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock JSON_12b_fetchroutingEchoFunction
|
||||
|
||||
!SUBSECTION Requests and Responses
|
||||
|
||||
|
@ -240,8 +297,8 @@ function (req, res, options, next) {
|
|||
|
||||
Install it via:
|
||||
|
||||
@startDocuBlockInline routingCreateEchoAction
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{routingCreateEchoAction}
|
||||
@startDocuBlockInline JSON_13_routingCreateEchoAction
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_13_routingCreateEchoAction}
|
||||
|db._routing.save({
|
||||
| url: "/echo",
|
||||
| action: {
|
||||
|
@ -250,22 +307,24 @@ Install it via:
|
|||
})
|
||||
~require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock routingCreateEchoAction
|
||||
@endDocuBlock JSON_13_routingCreateEchoAction
|
||||
|
||||
Reload the routing and check http:// 127.0.0.1:8529/hello/echo
|
||||
|
||||
You should see something like
|
||||
|
||||
@startDocuBlockInline fetchroutingRequestHelloEcho
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{fetchroutingRequestHelloEcho}
|
||||
arango.GET("hello/echo")
|
||||
@startDocuBlockInline JSON_14_fetchroutingRequestHelloEcho
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_14_fetchroutingRequestHelloEcho}
|
||||
arango.GET("/hello/echo")
|
||||
db._query("FOR route IN _routing FILTER route.url == '/hello/echo' REMOVE route in _routing")
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock fetchroutingRequestHelloEcho
|
||||
@endDocuBlock JSON_14_fetchroutingRequestHelloEcho
|
||||
|
||||
You may also pass options to the called function:
|
||||
|
||||
@startDocuBlockInline routingCreateEchoRequestOptions
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{routingCreateEchoRequestOptions}
|
||||
@startDocuBlockInline JSON_15_routingCreateEchoRequestOptions
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_15_routingCreateEchoRequestOptions}
|
||||
|db._routing.save({
|
||||
| url: "/echo",
|
||||
| action: {
|
||||
|
@ -277,17 +336,14 @@ You may also pass options to the called function:
|
|||
});
|
||||
~require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock routingCreateEchoRequestOptions
|
||||
@endDocuBlock JSON_15_routingCreateEchoRequestOptions
|
||||
|
||||
You should now see the options in the result.
|
||||
You now see the options in the result:
|
||||
|
||||
```js
|
||||
{
|
||||
"request": {
|
||||
...
|
||||
},
|
||||
"options": {
|
||||
"Hello": "World"
|
||||
}
|
||||
}
|
||||
````
|
||||
@startDocuBlockInline JSON_16_fetchroutingEchoRequestOptions
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{JSON_16_fetchroutingEchoRequestOptions}
|
||||
arango.GET("/echo")
|
||||
db._query("FOR route IN _routing FILTER route.url == '/echo' REMOVE route in _routing")
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock JSON_16_fetchroutingEchoRequestOptions
|
||||
|
|
|
@ -18,10 +18,10 @@ object is an action's only way to return data to the caller of the action.
|
|||
We've already seen how to set the HTTP status code, the content type, and the
|
||||
result body. The *res* object has the following properties for these:
|
||||
|
||||
- *contentType*: MIME type of the body as defined in the HTTP standard (e.g.
|
||||
- *contentType*: MIME type of the body as defined in the HTTP standard (e.g.
|
||||
*text/html*, *text/plain*, *application/json*, ...)
|
||||
- *responsecode*: the HTTP status code of the response as defined in the HTTP
|
||||
standard. Common values for actions that succeed are *200* or *201*.
|
||||
standard. Common values for actions that succeed are *200* or *201*.
|
||||
Please refer to the HTTP standard for more information.
|
||||
- *body*: the actual response data
|
||||
|
||||
|
@ -67,53 +67,69 @@ To write your own dynamic action handlers, you must put them into modules.
|
|||
Modules are a means of organizing action handlers and making them loadable under
|
||||
specific names.
|
||||
|
||||
To start, we'll define a simple action handler in a module */own/test*:
|
||||
To start, we'll define a simple action handler in a module */ownTest*:
|
||||
|
||||
```js
|
||||
arangosh> db._modules.save({
|
||||
path: "/own/test",
|
||||
content: "exports.do = function(req, res, options, next) { res.body = 'test'; res.responseCode = 200; res.contentType = 'text/html'; };"
|
||||
});
|
||||
```
|
||||
@startDocuBlockInline MOD_01a_routingCreateOwnTest
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_01a_routingCreateOwnTest}
|
||||
|db._modules.save({
|
||||
| path: "/db:/ownTest",
|
||||
| content:
|
||||
| "exports.do = function(req, res, options, next) {"+
|
||||
| " res.body = 'test';" +
|
||||
| " res.responseCode = 200;" +
|
||||
| " res.contentType = 'text/plain';" +
|
||||
| "};"
|
||||
});
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_01a_routingCreateOwnTest
|
||||
|
||||
This does nothing but register a do action handler in a module */own/test*. The
|
||||
This does nothing but register a do action handler in a module */ownTest*. The
|
||||
action handler is not yet callable, but must be mapped to a route first. To map
|
||||
the action to the route */ourtest*, execute the following command:
|
||||
|
||||
```js
|
||||
arangosh> db._routing.save({
|
||||
url: "/ourtest",
|
||||
action: {
|
||||
controller: "/own/test"
|
||||
}
|
||||
});
|
||||
```
|
||||
@startDocuBlockInline MOD_01b_routingEnableOwnTest
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_01b_routingEnableOwnTest}
|
||||
|db._routing.save({
|
||||
| url: "/ourtest",
|
||||
| action: {
|
||||
| controller: "db://ownTest"
|
||||
| }
|
||||
});
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_01b_routingEnableOwnTest
|
||||
|
||||
In order to see the module in action, you must either restart the server or call
|
||||
the internal reload function.
|
||||
Now use the browser or cURL and access http://localhost:8529/ourtest :
|
||||
|
||||
```js
|
||||
arangosh> require("internal").reloadRouting()
|
||||
```
|
||||
@startDocuBlockInline MOD_01c_routingCurlOwnTest
|
||||
@EXAMPLE_ARANGOSH_RUN{MOD_01c_routingCurlOwnTest}
|
||||
var url = "/ourtest";
|
||||
var response = logCurlRequest('GET', url);
|
||||
assert(response.code === 200);
|
||||
assert(response.body === 'test');
|
||||
logRawResponse(response);
|
||||
db._query("FOR route IN _routing FILTER route.url == '/ourtest' REMOVE route in _routing")
|
||||
db._query("FOR module IN _modules FILTER module.path == '/db:/ownTest' REMOVE module in _modules")
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_RUN
|
||||
@endDocuBlock MOD_01c_routingCurlOwnTest
|
||||
|
||||
Now use the browser and access http:// localhost:8529/ourtest
|
||||
|
||||
You will see that the module's do function has been executed.
|
||||
|
||||
!SECTION A Word about Caching
|
||||
|
||||
Sometimes it might seem that your change do not take effect. In this case the
|
||||
culprit could be one of the caches. With dynamic actions there are two caches
|
||||
involved:
|
||||
|
||||
!SUBSECTION The Routing Cache
|
||||
culprit could be the routing caches:
|
||||
|
||||
The routing cache stores the routing information computed from the *_routing*
|
||||
collection. Whenever you change this collection manually, you need to call
|
||||
|
||||
```js
|
||||
arangosh> require("internal").reloadRouting();
|
||||
```
|
||||
@startDocuBlockInline MOD_05_routingModifyReload
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_05_routingModifyReload}
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_05_routingModifyReload
|
||||
|
||||
in order to rebuild the cache.
|
||||
|
||||
|
@ -126,93 +142,222 @@ For detailed information see the reference manual.
|
|||
|
||||
Use the following for a permanent redirect:
|
||||
|
||||
```js
|
||||
arangosh> db._routing.save({
|
||||
url: "/",
|
||||
action: {
|
||||
do: "org/arangodb/actions/redirectRequest",
|
||||
options: {
|
||||
permanently: true,
|
||||
destination: "http://somewhere.else/"
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
@startDocuBlockInline MOD_06a_routingRedirect
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_06a_routingRedirect}
|
||||
| db._routing.save({
|
||||
| url: "/redirectMe",
|
||||
| action: {
|
||||
| do: "org/arangodb/actions/redirectRequest",
|
||||
| options: {
|
||||
| permanently: true,
|
||||
| destination: "/somewhere.else/"
|
||||
| }
|
||||
| }
|
||||
});
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_06a_routingRedirect
|
||||
|
||||
@startDocuBlockInline MOD_06b_routingCurlRedirect
|
||||
@EXAMPLE_ARANGOSH_RUN{MOD_06b_routingCurlRedirect}
|
||||
var url = "/redirectMe";
|
||||
var response = logCurlRequest('GET', url);
|
||||
assert(response.code === 301);
|
||||
logRawResponse(response);
|
||||
db._query("FOR route IN _routing FILTER route.url == '/redirectMe' REMOVE route in _routing")
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_RUN
|
||||
@endDocuBlock MOD_06b_routingCurlRedirect
|
||||
|
||||
!SUBSECTION Routing Bundles
|
||||
|
||||
Instead of adding all routes for package separately, you can
|
||||
specify a bundle.
|
||||
specify a bundle:
|
||||
|
||||
```js
|
||||
{
|
||||
routes: [
|
||||
{ url: "/url1", content: "..." },
|
||||
{ url: "/url2", content: "..." },
|
||||
{ url: "/url3", content: "..." },
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
@startDocuBlockInline MOD_07a_routingMulti
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_07a_routingMulti}
|
||||
| db._routing.save({
|
||||
| routes: [
|
||||
| {
|
||||
| url: "/url1",
|
||||
| content: "route 1"
|
||||
| },
|
||||
| {
|
||||
| url: "/url2",
|
||||
| content: "route 2"
|
||||
| },
|
||||
| {
|
||||
| url: "/url3",
|
||||
| content: "route 3"
|
||||
| }
|
||||
| ]
|
||||
});
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_07a_routingMulti
|
||||
|
||||
|
||||
@startDocuBlockInline MOD_07b_routingCurlMulti
|
||||
@EXAMPLE_ARANGOSH_RUN{MOD_07b_routingCurlMulti}
|
||||
var url = ["/url1", "/url2", "/url3"];
|
||||
var reply = ["route 1", "route 2", "route 3"]
|
||||
for (var i = 1; i < 3; i++) {
|
||||
var response = logCurlRequest('GET', url[i]);
|
||||
assert(response.code === 200);
|
||||
assert(response.body === reply[i])
|
||||
logRawResponse(response);
|
||||
}
|
||||
db._query("FOR route IN _routing FILTER route.routes[0].url == '/url1' REMOVE route in _routing")
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_RUN
|
||||
@endDocuBlock MOD_07b_routingCurlMulti
|
||||
|
||||
The advantage is, that you can put all your routes into one document
|
||||
and use a common prefix.
|
||||
|
||||
```js
|
||||
{
|
||||
urlPrefix: "/test",
|
||||
routes: [
|
||||
{ url: "/url1", content: "..." },
|
||||
{ url: "/url2", content: "..." },
|
||||
{ url: "/url3", content: "..." },
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
will define the URL */test/url1*, */test/url2*, and */test/url3*.
|
||||
@startDocuBlockInline MOD_07c_routingMulti
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_07c_routingMulti}
|
||||
| db._routing.save({
|
||||
| urlPrefix: "/test",
|
||||
| routes: [
|
||||
| {
|
||||
| url: "/url1",
|
||||
| content: "route 1"
|
||||
| },
|
||||
| {
|
||||
| url: "/url2",
|
||||
| content: "route 2"
|
||||
| },
|
||||
| {
|
||||
| url: "/url3",
|
||||
| content: "route 3"
|
||||
| }
|
||||
| ]
|
||||
});
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_07c_routingMulti
|
||||
|
||||
will define the URL */test/url1*, */test/url2*, and */test/url3*:
|
||||
|
||||
@startDocuBlockInline MOD_07d_routingCurlMulti
|
||||
@EXAMPLE_ARANGOSH_RUN{MOD_07d_routingCurlMulti}
|
||||
var url = ["/test/url1", "/test/url2", "/test/url3"];
|
||||
var reply = ["route 1", "route 2", "route 3"]
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var response = logCurlRequest('GET', url[i]);
|
||||
assert(response.code === 200);
|
||||
assert(response.body === reply[i])
|
||||
logRawResponse(response);
|
||||
}
|
||||
db._query("FOR route IN _routing FILTER route.routes[0].url == '/url1' REMOVE route in _routing")
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_RUN
|
||||
@endDocuBlock MOD_07d_routingCurlMulti
|
||||
|
||||
!SUBSECTION Writing Middleware
|
||||
|
||||
Assume, you want to log every request. In this case you can easily define an
|
||||
action for the whole url-space */*. This action simply logs the requests, calls
|
||||
the next in line, and logs the response.
|
||||
Assume, you want to log every request in your namespace to the console. *(if ArangoDB is running
|
||||
as a daemon, this will end up in the logfile)*. In this case you can easily define an
|
||||
action for the URL */subdirectory*. This action simply logs
|
||||
the requests, calls the next in line, and logs the response:
|
||||
|
||||
```js
|
||||
exports.logRequest = function (req, res, options, next) {
|
||||
console.log("received request: %s", JSON.stringify(req));
|
||||
next();
|
||||
console.log("produced response: %s", JSON.stringify(res));
|
||||
};
|
||||
```
|
||||
@startDocuBlockInline MOD_08a_routingCreateOwnConsoleLog
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_08a_routingCreateOwnConsoleLog}
|
||||
|db._modules.save({
|
||||
| path: "/db:/OwnMiddlewareTest",
|
||||
| content:
|
||||
| "exports.logRequest = function (req, res, options, next) {" +
|
||||
| " console = require('console'); " +
|
||||
| " console.log('received request: %s', JSON.stringify(req));" +
|
||||
| " next();" +
|
||||
| " console.log('produced response: %s', JSON.stringify(res));" +
|
||||
| "};"
|
||||
});
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_08a_routingCreateOwnConsoleLog
|
||||
|
||||
This function is available as *org/arangodb/actions/logRequest*. You need to
|
||||
This function will now be available as *db://OwnMiddlewareTest/logRequest*. You need to
|
||||
tell ArangoDB that it is should use a prefix match and that the shortest match
|
||||
should win in this case:
|
||||
|
||||
```js
|
||||
arangosh> db._routing.save({
|
||||
middleware: [
|
||||
{
|
||||
url: {
|
||||
match: "/*"
|
||||
},
|
||||
action: {
|
||||
do: "org/arangodb/actions/logRequest"
|
||||
}
|
||||
}
|
||||
]
|
||||
@startDocuBlockInline MOD_08b_routingCreateRouteToOwnConsoleLog
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_08b_routingCreateRouteToOwnConsoleLog}
|
||||
|db._routing.save({
|
||||
| middleware: [
|
||||
| {
|
||||
| url: {
|
||||
| match: "/subdirectory/*"
|
||||
| },
|
||||
| action: {
|
||||
| do: "db://OwnMiddlewareTest/logRequest"
|
||||
| }
|
||||
| }
|
||||
| ]
|
||||
});
|
||||
```
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_08b_routingCreateRouteToOwnConsoleLog
|
||||
|
||||
If you call *next()*, the next specific routing will be used for the
|
||||
original URL. Even if you modify the URL in the request object *req*,
|
||||
this will not cause the *next()* to jump to the routing defined for
|
||||
this next URL. If proceeds occurring the origin URL. However, if you
|
||||
use *next(true)*, the routing will stop and request handling is
|
||||
When you call *next()* in that action, the next specific routing will
|
||||
be used for the original URL. Even if you modify the URL in the request
|
||||
object *req*, this will not cause the *next()* to jump to the routing
|
||||
defined for this next URL. If proceeds occurring the origin URL. However,
|
||||
if you use *next(true)*, the routing will stop and request handling is
|
||||
started with the new URL. You must ensure that *next(true)* is never
|
||||
called without modifying the URL in the request object
|
||||
*req*. Otherwise an endless loop will occur.
|
||||
|
||||
Now we add some other simple routings to test all this:
|
||||
|
||||
@startDocuBlockInline MOD_08c_routingCreateRouteToOwnConsoleLog
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_08c_routingCreateRouteToOwnConsoleLog}
|
||||
|db._routing.save({
|
||||
| url: "/subdirectory/ourtest/1",
|
||||
| action: {
|
||||
| do: "org/arangodb/actions/echoRequest"
|
||||
| }
|
||||
});
|
||||
|db._routing.save({
|
||||
| url: "/subdirectory/ourtest/2",
|
||||
| action: {
|
||||
| do: "org/arangodb/actions/echoRequest"
|
||||
| }
|
||||
});
|
||||
|db._routing.save({
|
||||
| url: "/subdirectory/ourtest/3",
|
||||
| action: {
|
||||
| do: "org/arangodb/actions/echoRequest"
|
||||
| }
|
||||
});
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_08c_routingCreateRouteToOwnConsoleLog
|
||||
|
||||
Then we send some curl requests to these sample routes:
|
||||
|
||||
@startDocuBlockInline MOD_08d_routingCurlToOwnConsoleLog
|
||||
@EXAMPLE_ARANGOSH_RUN{MOD_08d_routingCurlToOwnConsoleLog}
|
||||
var url = ["/subdirectory/ourtest/1",
|
||||
"/subdirectory/ourtest/2",
|
||||
"/subdirectory/ourtest/3"];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var response = logCurlRequest('GET', url[i]);
|
||||
assert(response.code === 200);
|
||||
logJsonResponse(response);
|
||||
}
|
||||
db._query("FOR route IN _routing FILTER route.middleware[0].url.match == '/subdirectory/*' REMOVE route in _routing");
|
||||
db._query("FOR route IN _routing FILTER route.url == '/subdirectory/ourtest/1' REMOVE route in _routing");
|
||||
db._query("FOR route IN _routing FILTER route.url == '/subdirectory/ourtest/2' REMOVE route in _routing");
|
||||
db._query("FOR route IN _routing FILTER route.url == '/subdirectory/ourtest/3' REMOVE route in _routing");
|
||||
db._query("FOR module IN _modules FILTER module.path == '/db:/OwnMiddlewareTest' REMOVE module in _modules");
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_RUN
|
||||
@endDocuBlock MOD_08d_routingCurlToOwnConsoleLog
|
||||
|
||||
and the console (and / or the logfile) will show requests and replies.
|
||||
*Note that logging doesn't warant the sequence in which these lines
|
||||
will appear.*
|
||||
|
||||
!SECTION Application Deployment
|
||||
|
||||
Using single routes or [bundles](#routing_bundles) can be
|
||||
|
@ -222,19 +367,21 @@ Note that there is also [Foxx](../Foxx/README.md) for building applications
|
|||
with ArangoDB.
|
||||
|
||||
!SECTION Common Pitfalls when using Actions
|
||||
|
||||
|
||||
!SUBSECTION Caching
|
||||
|
||||
If you made any changes to the routing but the changes do not have any effect
|
||||
when calling the modified action's URL, you might have been hit by some
|
||||
caching issues.
|
||||
If you made any changes to the routing but the changes does not have any effect
|
||||
when calling the modified actions URL, you might have been hit by some
|
||||
caching issues.
|
||||
|
||||
After any modification to the routing or actions, it is thus recommended to
|
||||
make the changes "live" by calling the following functions from within arangosh:
|
||||
|
||||
```js
|
||||
arangosh> require("internal").reloadRouting();
|
||||
```
|
||||
@startDocuBlockInline MOD_09_routingReload
|
||||
@EXAMPLE_ARANGOSH_RUN{MOD_09_routingReload}
|
||||
require("internal").reloadRouting();
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_09_routingReload
|
||||
|
||||
You might also be affected by client-side caching.
|
||||
Browsers tend to cache content and also redirection URLs. You might need to
|
||||
|
@ -261,37 +408,68 @@ then the header *X-My-Value* will have a value of (string) *5* and not (number)
|
|||
|
||||
!SUBSECTION 501 Not Implemented
|
||||
|
||||
If you defined a URL in the routing and the URL is accessible fine via
|
||||
HTTP *GET* but returns an HTTP 501 (not implemented) for other HTTP methods
|
||||
If you defined a URL in the routing and the URL is accessible fine via
|
||||
HTTP *GET* but returns an HTTP 501 (not implemented) for other HTTP methods
|
||||
such as *POST*, *PUT* or *DELETE*, then you might have been hit by some
|
||||
defaults.
|
||||
|
||||
By default, URLs defined like this (simple string *url* attribute) are
|
||||
By default, URLs defined like this (simple string *url* attribute) are
|
||||
accessible via HTTP *GET* and *HEAD* only. To make such URLs accessible via
|
||||
other HTTP methods, extend the URL definition with the *methods* attribute.
|
||||
|
||||
For example, this definition only allows access via *GET* and *HEAD*:
|
||||
|
||||
```js
|
||||
{
|
||||
url: "/hello/world"
|
||||
{
|
||||
url: "/hello/world"
|
||||
}
|
||||
```
|
||||
|
||||
whereas this definition allows HTTP *GET*, *POST*, and *PUT*:
|
||||
|
||||
```js
|
||||
{
|
||||
url: {
|
||||
match: "/hello/world",
|
||||
methods: [ "get", "post", "put" ]
|
||||
}
|
||||
}
|
||||
```
|
||||
@startDocuBlockInline MOD_09a_routingSpecifyMethods
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MOD_09a_routingSpecifyMethods}
|
||||
|db._routing.save({
|
||||
| url: {
|
||||
| match: "/hello/world",
|
||||
| methods: [ "get", "post", "put" ]
|
||||
| },
|
||||
| action: {
|
||||
| do: "org/arangodb/actions/echoRequest"
|
||||
| }
|
||||
});
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MOD_09a_routingSpecifyMethods
|
||||
|
||||
@startDocuBlockInline MOD_09b_routingCurlSpecifyMethods
|
||||
@EXAMPLE_ARANGOSH_RUN{MOD_09b_routingCurlSpecifyMethods}
|
||||
var url = "/hello/world"
|
||||
var postContent = "{hello: 'world'}";
|
||||
var response = logCurlRequest('GET', url);
|
||||
assert(response.code === 200);
|
||||
logJsonResponse(response);
|
||||
|
||||
var response = logCurlRequest('POST', url, postContent);
|
||||
assert(response.code === 200);
|
||||
logJsonResponse(response);
|
||||
|
||||
var response = logCurlRequest('PUT', url, postContent);
|
||||
assert(response.code === 200);
|
||||
logJsonResponse(response);
|
||||
|
||||
var response = logCurlRequest('DELETE', url);
|
||||
assert(response.code === 404); //// TODO: should be 405 - method not allowed
|
||||
logJsonResponse(response);
|
||||
|
||||
db._query("FOR route IN _routing FILTER route.url.match == '/hello/world' REMOVE route in _routing");
|
||||
require("internal").reloadRouting()
|
||||
@END_EXAMPLE_ARANGOSH_RUN
|
||||
@endDocuBlock MOD_09b_routingCurlSpecifyMethods
|
||||
|
||||
The former definition (defining *url* as an object with a *match* attribute)
|
||||
will result in the URL being accessible via all supported HTTP methods (e.g.
|
||||
*GET*, *POST*, *PUT*, *DELETE*, ...), whereas the latter definition (providing a string
|
||||
*url* attribute) will result in the URL being accessible via HTTP *GET* and
|
||||
*url* attribute) will result in the URL being accessible via HTTP *GET* and
|
||||
HTTP *HEAD* only, with all other HTTP methods being disabled. Calling a URL
|
||||
with an unsupported or disabled HTTP method will result in an HTTP 501 error.
|
||||
with an unsupported or disabled HTTP method will result in an HTTP 404 error.
|
||||
|
|
|
@ -12,11 +12,11 @@ fashion. The collection *_users* contains all users and a salted SHA256 hash
|
|||
of their passwords. A user can be active or inactive. A typical document of this
|
||||
collection is
|
||||
|
||||
@startDocuBlockInline authFetch
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{authFetch}
|
||||
@startDocuBlockInline USER_01_authFetch
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{USER_01_authFetch}
|
||||
db._users.toArray()
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock authFetch
|
||||
@endDocuBlock USER_01_authFetch
|
||||
|
||||
!SUBSECTION Command-Line Options for the Authentication and Authorization
|
||||
|
||||
|
@ -62,9 +62,32 @@ the server authentication cache is [reloaded](#examples_reload).
|
|||
|
||||
*Examples*
|
||||
|
||||
```js
|
||||
arangosh> require("org/arangodb/users").save("my-user", "my-secret-password");
|
||||
```
|
||||
@startDocuBlockInline USER_02_saveUser
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{USER_02_saveUser}
|
||||
require("org/arangodb/users").save("my-user", "my-secret-password");
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock USER_02_saveUser
|
||||
|
||||
!SUBSECTION Reload
|
||||
|
||||
`users.reload()`
|
||||
|
||||
Reloads the user authentication data on the server
|
||||
|
||||
All user authentication data is loaded by the server once on startup only and is
|
||||
cached after that. When users get added or deleted, a cache flush is required,
|
||||
and this can be performed by called this method.
|
||||
|
||||
*Examples*
|
||||
|
||||
@startDocuBlockInline USER_03_reloadUser
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{USER_03_reloadUser}
|
||||
require("org/arangodb/users").reload();
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock USER_03_reloadUser
|
||||
|
||||
**Note**: this function will not work from within the web interface
|
||||
|
||||
|
||||
!SUBSECTION Document
|
||||
|
||||
|
@ -76,6 +99,14 @@ The username must be specified in *user*.
|
|||
|
||||
This method will fail if the user cannot be found in the database.
|
||||
|
||||
*Examples*
|
||||
|
||||
@startDocuBlockInline USER_04_documentUser
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{USER_04_documentUser}
|
||||
require("org/arangodb/users").document("my-user");
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock USER_04_documentUser
|
||||
|
||||
**Note**: this function will not work from within the web interface
|
||||
|
||||
!SUBSECTION Replace
|
||||
|
@ -104,9 +135,11 @@ database.
|
|||
|
||||
*Examples*
|
||||
|
||||
```js
|
||||
arangosh> require("org/arangodb/users").replace("my-user", "my-changed-password");
|
||||
```
|
||||
@startDocuBlockInline USER_03_replaceUser
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{USER_03_replaceUser}
|
||||
require("org/arangodb/users").replace("my-user", "my-changed-password");
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock USER_03_replaceUser
|
||||
|
||||
!SUBSECTION Update
|
||||
|
||||
|
@ -131,9 +164,46 @@ database.
|
|||
|
||||
*Examples*
|
||||
|
||||
```js
|
||||
arangosh> require("org/arangodb/users").update("my-user", "my-secret-password");
|
||||
```
|
||||
@startDocuBlockInline USER_04_updateUser
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{USER_04_updateUser}
|
||||
require("org/arangodb/users").update("my-user", "my-secret-password");
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock USER_04_updateUser
|
||||
|
||||
!SUBSECTION isValid
|
||||
|
||||
`users.isValid(user, password)`
|
||||
|
||||
Checks whether the given combination of username and password is valid. The
|
||||
function will return a boolean value if the combination of username and password
|
||||
is valid.
|
||||
|
||||
Each call to this function is penalized by the server sleeping a random
|
||||
amount of time.
|
||||
|
||||
*Examples*
|
||||
|
||||
@startDocuBlockInline USER_05_isValidUser
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{USER_05_isValidUser}
|
||||
require("org/arangodb/users").isValid("my-user", "my-secret-password");
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock USER_05_isValidUser
|
||||
|
||||
**Note**: this function will not work from within the web interface
|
||||
|
||||
!SUBSECTION all()
|
||||
|
||||
`users.all()`
|
||||
|
||||
Fetches all existing ArangoDB users from the database.
|
||||
|
||||
*Examples*
|
||||
|
||||
@startDocuBlockInline USER_06_AllUsers
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{USER_06_AllUsers}
|
||||
require("org/arangodb/users").all();
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock USER_06_AllUsers
|
||||
|
||||
!SUBSECTION Remove
|
||||
|
||||
|
@ -150,37 +220,9 @@ This method will fail if the user cannot be found in the database.
|
|||
|
||||
*Examples*
|
||||
|
||||
```js
|
||||
arangosh> require("org/arangodb/users").remove("my-user");
|
||||
```
|
||||
@startDocuBlockInline USER_07_removeUser
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{USER_07_removeUser}
|
||||
require("org/arangodb/users").remove("my-user");
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock USER_07_removeUser
|
||||
|
||||
!SUBSECTION Reload
|
||||
|
||||
`users.reload()`
|
||||
|
||||
Reloads the user authentication data on the server
|
||||
|
||||
All user authentication data is loaded by the server once on startup only and is
|
||||
cached after that. When users get added or deleted, a cache flush is required,
|
||||
and this can be performed by called this method.
|
||||
|
||||
**Note**: this function will not work from within the web interface
|
||||
|
||||
!SUBSECTION isValid
|
||||
|
||||
`users.isvalid(user, password)`
|
||||
|
||||
Checks whether the given combination of username and password is valid. The
|
||||
function will return a boolean value if the combination of username and password
|
||||
is valid.
|
||||
|
||||
Each call to this function is penalized by the server sleeping a random
|
||||
amount of time.
|
||||
|
||||
**Note**: this function will not work from within the web interface
|
||||
|
||||
!SUBSECTION all()
|
||||
|
||||
`users.all()`
|
||||
|
||||
Fetches all existing ArangoDB users from the database.
|
||||
|
|
|
@ -169,7 +169,7 @@ the `_id` using *byExample*:
|
|||
@EXAMPLE_ARANGOSH_OUTPUT{07_workWithColl_remove}
|
||||
db.example.remove(db.example.byExample({ name: "John Doe" }).toArray()[0]._id)
|
||||
db.example.toArray()
|
||||
~addIgnoreCollection("example")
|
||||
~removeIgnoreCollection("example")
|
||||
~db._drop("example")
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock 07_workWithColl_remove
|
||||
|
|
|
@ -150,10 +150,12 @@ arangosh> test1.func1();
|
|||
The values in `__filename` and `__dirname` can be used for generic debugging and for
|
||||
creating filename relative to the required file, e.g.
|
||||
|
||||
```js
|
||||
var fs = require("fs");
|
||||
var relativeFile = fs.join(__dirname, "scripts", "test.js");
|
||||
```
|
||||
@startDocuBlockInline MODJS_fsDir
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{MODJS_fsDir}
|
||||
var files = require("fs");
|
||||
relativeFile = files.join(__dirname, "scripts", "test.js");
|
||||
@END_EXAMPLE_ARANGOSH_OUTPUT
|
||||
@endDocuBlock MODJS_fsDir
|
||||
|
||||
|
||||
*require* can also be used to load JSON data. If the name of the required module ends
|
||||
|
|
|
@ -30,3 +30,13 @@ Returns the current query tracking configuration.
|
|||
`require("org/arangodb/aql/queries").kill();`
|
||||
|
||||
Kills a running AQL query.
|
||||
|
||||
|
||||
|
||||
var tasks = require("org/arangodb/tasks");
|
||||
tasks.register({
|
||||
id: "mytask-1",
|
||||
name: "this is a snippet task",
|
||||
period: 15,
|
||||
command: "require('console').log('hello from snippet task');"
|
||||
});
|
Loading…
Reference in New Issue