Skip to content

Commit f45fc8c

Browse files
authored
Add query management API (#590)
* Add query management API * Fix parse/explain results * Document query API * Add tests
1 parent 4dc7a8a commit f45fc8c

File tree

7 files changed

+635
-108
lines changed

7 files changed

+635
-108
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2323
and ArangoDB will still continue processing the request, this will merely
2424
result in the socket being forcefully disconnected.
2525

26+
- Added query management API
27+
28+
This implements most endpoints of https://docs.arangodb.com/3.4/HTTP/AqlQuery/.
29+
2630
## [6.9.0] - 2018-11-07
2731

2832
### Changed

docs/Drivers/JS/Reference/Aql.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# AQL Helpers
2+
3+
These helpers are available via the `aql` export from the arangojs module:
4+
5+
```js
6+
import arangojs, { aql } from "arangojs";
7+
8+
// or CommonJS:
9+
10+
const arangojs = require("arangojs");
11+
const aql = arangojs.aql;
12+
```
13+
14+
## aql
15+
16+
`aql: AqlQuery`
17+
18+
The `aql` function is a JavaScript template string handler (or template tag).
19+
It can be used to write complex AQL queries as multi-line strings without
20+
having to worry about bindVars and the distinction between collections
21+
and regular parameters.
22+
23+
To use it just prefix a JavaScript template string (the ones with backticks
24+
instead of quotes) with its import name (e.g. `aql`) and pass in variables
25+
like you would with a regular template string. The string will automatically
26+
be converted into an object with `query` and `bindVars` attributes which you
27+
can pass directly to `db.query` to execute. If you pass in a collection it
28+
will be automatically recognized as a collection reference
29+
and handled accordingly.
30+
31+
The `aql` template tag can also be used inside other `aql` template strings,
32+
allowing arbitrary nesting. Bind parameters of nested queries will be merged
33+
automatically.
34+
35+
**Examples**
36+
37+
```js
38+
const filterValue = 23;
39+
const mydata = db.collection("mydata");
40+
const result = await db.query(aql`
41+
FOR d IN ${mydata}
42+
FILTER d.num > ${filterValue}
43+
RETURN d
44+
`);
45+
46+
// nested queries
47+
48+
const color = "green";
49+
const filterByColor = aql`FILTER d.color == ${color}'`;
50+
const result2 = await db.query(aql`
51+
FOR d IN ${mydata}
52+
${filterByColor}
53+
RETURN d
54+
`);
55+
```
56+
57+
## aql.literal
58+
59+
`aql.literal(value): AqlLiteral`
60+
61+
The `aql.literal` helper can be used to mark strings to be inlined into an AQL
62+
query when using the `aql` template tag, rather than being treated as a bind
63+
parameter.
64+
65+
{% hint 'danger' %}
66+
Any value passed to `aql.literal` will be treated as part of the AQL query.
67+
To avoid becoming vulnerable to AQL injection attacks you should always prefer
68+
nested `aql` queries if possible.
69+
{% endhint %}
70+
71+
**Arguments**
72+
73+
- **value**: `string`
74+
75+
An arbitrary string that will be treated as a literal AQL fragment when used
76+
in an `aql` template.
77+
78+
**Examples**
79+
80+
```js
81+
const filterGreen = aql.literal('FILTER d.color == "green"');
82+
const result = await db.query(aql`
83+
FOR d IN ${mydata}
84+
${filterGreen}
85+
RETURN d
86+
`);
87+
```
88+
89+
## aql.join
90+
91+
`aql.join(values)`
92+
93+
The `aql.join` helper takes an array of queries generated using the `aql` tag
94+
and combines them into a single query. The optional second argument will be
95+
used as literal string to combine the queries.
96+
97+
**Arguments**
98+
99+
- **values**: `Array`
100+
101+
An array of arbitrary values, typically AQL query objects or AQL literals.
102+
103+
- **sep**: `string` (Default: `" "`)
104+
105+
String that will be used in between the values.
106+
107+
**Examples**
108+
109+
```js
110+
// Basic usage
111+
const parts = [aql`FILTER`, aql`x`, aql`%`, aql`2`];
112+
const joined = aql.join(parts); // aql`FILTER x % 2`
113+
114+
// Merge without the extra space
115+
const parts = [aql`FIL`, aql`TER`];
116+
const joined = aql.join(parts, ""); // aql`FILTER`;
117+
118+
// Real world example: translate keys into document lookups
119+
const users = db.collection("users");
120+
const keys = ["abc123", "def456"];
121+
const docs = keys.map(key => aql`DOCUMENT(${users}, ${key})`);
122+
const aqlArray = aql`[${aql.join(docs, ", ")}]`;
123+
const result = await db.query(aql`
124+
FOR d IN ${aqlArray}
125+
RETURN d
126+
`);
127+
// Query:
128+
// FOR d IN [DOCUMENT(@@value0, @value1), DOCUMENT(@@value0, @value2)]
129+
// RETURN d
130+
// Bind parameters:
131+
// @value0: "users"
132+
// value1: "abc123"
133+
// value2: "def456"
134+
135+
// Alternative without `aql.join`
136+
const users = db.collection("users");
137+
const keys = ["abc123", "def456"];
138+
const result = await db.query(aql`
139+
FOR key IN ${keys}
140+
LET d = DOCUMENT(${users}, key)
141+
RETURN d
142+
`);
143+
// Query:
144+
// FOR key IN @value0
145+
// LET d = DOCUMENT(@@value1, key)
146+
// RETURN d
147+
// Bind parameters:
148+
// value0: ["abc123", "def456"]
149+
// @value1: "users"
150+
```

docs/Drivers/JS/Reference/Database/Queries.md

Lines changed: 112 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Queries
22

3-
This function implements the
4-
[HTTP API for single round-trip AQL queries](https://docs.arangodb.com/latest/HTTP/AqlQueryCursor/QueryResults.html).
3+
These functions implements the
4+
[HTTP API for single round-trip AQL queries](https://docs.arangodb.com/latest/HTTP/AqlQueryCursor/QueryResults.html)
5+
as well as the
6+
[HTTP API for managing queries](https://docs.arangodb.com/latest/HTTP/AqlQuery/index.html).
57

68
For collection-specific queries see [Simple Queries](../Collection/SimpleQueries.md).
79

@@ -14,10 +16,13 @@ Performs a database query using the given _query_ and _bindVars_, then returns a
1416

1517
**Arguments**
1618

17-
- **query**: `string`
19+
- **query**: `string | AqlQuery | AqlLiteral`
1820

19-
An AQL query string or a [query builder](https://npmjs.org/package/aqb)
20-
instance.
21+
An AQL query as a string or
22+
[AQL query object](../Aql.md#aql) or
23+
[AQL literal](../Aql.md#aqlliteral).
24+
If the query is an AQL query object, the second argument is treated as the
25+
_opts_ argument instead of _bindVars_.
2126

2227
- **bindVars**: `Object` (optional)
2328

@@ -139,3 +144,105 @@ const query = aql`
139144
`;
140145
// FILTER user.email == @value0
141146
```
147+
148+
## database.explain
149+
150+
`async database.explain(query, [bindVars,] [opts]): ExplainResult`
151+
152+
Explains a database query using the given _query_ and _bindVars_ and
153+
returns one or more plans.
154+
155+
**Arguments**
156+
157+
- **query**: `string | AqlQuery | AqlLiteral`
158+
159+
An AQL query as a string or
160+
[AQL query object](../Aql.md#aql) or
161+
[AQL literal](../Aql.md#aqlliteral).
162+
If the query is an AQL query object, the second argument is treated as the
163+
_opts_ argument instead of _bindVars_.
164+
165+
- **bindVars**: `Object` (optional)
166+
167+
An object defining the variables to bind the query to.
168+
169+
- **opts**: `Object` (optional)
170+
171+
- **optimizer**: `Object` (optional)
172+
173+
An object with a single property **rules**, a string array of optimizer
174+
rules to be used for the query.
175+
176+
- **maxNumberOfPlans**: `number` (optional)
177+
178+
Maximum number of plans that the optimizer is allowed to generate.
179+
Setting this to a low value limits the amount of work the optimizer does.
180+
181+
- **allPlans**: `boolean` (Default: `false`)
182+
183+
If set to true, all possible execution plans will be returned
184+
as the _plans_ property. Otherwise only the optimal execution plan will
185+
be returned as the _plan_ property.
186+
187+
## database.parse
188+
189+
`async database.parse(query): ParseResult`
190+
191+
Parses the given query and returns the result.
192+
193+
**Arguments**
194+
195+
- **query**: `string | AqlQuery | AqlLiteral`
196+
197+
An AQL query as a string or
198+
[AQL query object](../Aql.md#aql) or
199+
[AQL literal](../Aql.md#aqlliteral).
200+
If the query is an AQL query object, its bindVars (if any) will be ignored.
201+
202+
## database.queryTracking
203+
204+
`async database.queryTracking(): QueryTrackingProperties`
205+
206+
Fetches the query tracking properties.
207+
208+
## database.setQueryTracking
209+
210+
`async database.setQueryTracking(props): void`
211+
212+
Modifies the query tracking properties.
213+
214+
**Arguments**
215+
216+
- **props**: `Partial<QueryTrackingProperties>`
217+
218+
Query tracking properties with new values to set.
219+
220+
## database.listRunningQueries
221+
222+
`async database.listRunningQueries(): Array<QueryStatus>`
223+
224+
Fetches a list of information for all currently running queries.
225+
226+
## database.listSlowQueries
227+
228+
`async database.listSlowQueries(): Array<SlowQueryStatus>`
229+
230+
Fetches a list of information for all recent slow queries.
231+
232+
## database.clearSlowQueries
233+
234+
`async database.clearSlowQueries(): void`
235+
236+
Clears the list of recent slow queries.
237+
238+
## database.killQuery
239+
240+
`async database.killQuery(queryId): void`
241+
242+
Kills a running query with the given ID.
243+
244+
**Arguments**
245+
246+
- **queryId**: `string`
247+
248+
The ID of a currently running query.

docs/Drivers/JS/Reference/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- [Indexes](Collection/Indexes.md)
1919
- [Simple Queries](Collection/SimpleQueries.md)
2020
- [Bulk Import](Collection/BulkImport.md)
21+
- [AQL Helpers](Aql.md)
2122
- [View Manipulation](ViewManipulation.md)
2223
- [Cursor](Cursor.md)
2324
- [Graph](Graph/README.md)

0 commit comments

Comments
 (0)