92 KiB
ArangoDB's Query Language (AQL)
@NAVIGATE_Aql @EMBEDTOC{AqlTOC}
Introduction
The ArangoDB query language (AQL) can be used to retrieve data that is stored in ArangoDB. The general workflow when executing a query is as follows:
- A client application ships an AQL query to the ArangoDB server. The query text contains everything ArangoDB needs to compile the result set
- ArangoDB will parse the query, execute it and compile the results. If the query is invalid or cannot be executed, the server will return an error that the client can process and react to. If the query can be executed successfully, the server will return the query results to the client
AQL is mainly a declarative language, meaning that in a query it is expressed what result should be achieved and not how. AQL aims to be human- readable and therefore uses keywords from the English language. Another design goal of AQL was client independency, meaning that the language and syntax are the same for all clients, no matter what programming language the clients might use. Further design goals of AQL were to support complex query patterns, and to support the different data models ArangoDB offers.
In its purpose, AQL is similar to the Structured Query Language (SQL), but the two languages have major syntactic differences. Furthermore, to avoid any confusion between the two languages, the keywords in AQL have been chosen to be different from the keywords used in SQL.
AQL currently supports reading data only. That means you can use the language to issue read-requests on your database, but modifying data via AQL is currently not supported.
For some example queries, please refer to the page @ref AqlExamples.
How to invoke AQL
You can run AQL queries from your application via the HTTP REST API. The full API description is available at @ref HttpCursor.
You can also run AQL queries from arangosh. To do so, first create an ArangoStatement object as follows:
arangosh> stmt = db._createStatement( { "query": "FOR i IN [ 1, 2 ] RETURN i * 2" } );
[object ArangoStatement]
To execute the query, use the execute
method:
arangosh> c = stmt.execute();
[object ArangoQueryCursor]
This has executed the query. The query results are available in a cursor
now. The cursor can return all its results at once using the toArray
method.
This is a short-cut that you can use if you want to access the full result
set without iterating over it yourself.
arangosh> c.toArray();
[2, 4]
Cursors can also be used to iterate over the result set document-by-document.
To do so, use the hasNext
and next
methods of the cursor:
arangosh> while (c.hasNext()) { require("internal").print(c.next()); }
2
4
Please note that you can iterate over the results of a cursor only once, and that the cursor will be empty when you have fully iterated over it. To iterate over the results again, the query needs to be re-executed.
Additionally, the iteration can be done in a forward-only fashion. There is no backwards iteration or random access to elements in a cursor.
To execute an AQL query using bind parameters, you need to create a statement first and then bind the parameters to it before execution:
arangosh> stmt = db._createStatement( { "query": "FOR i IN [ @one, @two ] RETURN i * 2" } );
[object ArangoStatement]
arangosh> stmt.bind("one", 1);
arangosh> stmt.bind("two", 2);
arangosh> c = stmt.execute();
[object ArangoQueryCursor]
The cursor results can then be dumped or iterated over as usual, e.g.:
arangosh> c.toArray();
[2, 4]
or
arangosh> while (c.hasNext()) { require("internal").print(c.next()); }
2
4
Please note that bind variables can also be passed into the _createStatement
method directly,
making it a bit more convenient:
arangosh> stmt = db._createStatement( {
"query": "FOR i IN [ @one, @two ] RETURN i * 2",
"bindVars": {
"one": 1,
"two": 2
}
} );
Cursors also optionally provide the total number of results. By default, they do not.
To make the server return the total number of results, you may set the count
attribute to
true
when creating a statement:
arangosh> stmt = db._createStatement( { "query": "FOR i IN [ 1, 2, 3, 4 ] RETURN i", "count": true } );
After executing this query, you can use the count
method of the cursor to get the
number of total results from the result set:
arangosh> c = stmt.execute();
[object ArangoQueryCursor]
arangosh> c.count();
4
Please note that the count
method returns nothing if you did not specify the count
attribute when creating the query.
This is intentional so that the server may apply optimizations when executing the query and
construct the result set incrementally. Incremental creating of the result sets would not be possible
if the total number of results needs to be shipped to the client anyway. Therefore, the client
has the choice to specify count
and retrieve the total number of results for a query (and
disable potential incremental result set creation on the server), or to not retrieve the total
number of results and allow the server to apply optimizations.
Please note that at the moment the server will always create the full result set for each query so
specifying or omitting the count
attribute currently does not have any impact on query execution.
This might change in the future. Future versions of ArangoDB might create result sets incrementally
on the server-side and might be able to apply optimizations if a result set is not fully fetched by
a client.
Query results
Result sets
The result of an AQL query is a list of values. The individual values in the result list may or may not have a homogeneous structure, depending on what is actually queried.
For example, when returning data from a collection with inhomogeneous documents (the individual documents in the collection have different attribute names) without modification, the result values will as well have an inhomogeneous structure. Each result value itself is a document:
FOR u IN users
RETURN u
[ { "id" : 1, "name" : "John", "active" : false },
{ "age" : 32, "id" : 2, "name" : "Vanessa" },
{ "friends" : [ "John", "Vanessa" ], "id" : 3, "name" : "Amy" } ]
However, if a fixed set of attributes from the collection is queried, then the query result values will have a homogeneous structure. Each result value is still a document:
FOR u IN users
RETURN { "id" : u.id, "name" : u.name }
[ { "id" : 1, "name" : "John" },
{ "id" : 2, "name" : "Vanessa" },
{ "id" : 3, "name" : "Amy" } ]
It is also possible to query just scalar values. In this case, the result set is a list of scalars, and each result value is a scalar value:
FOR u IN users
RETURN u.id
[ 1, 2, 3 ]
If a query does not produce any results because no matching data can be found, it will produce an empty result list:
[ ]
Errors
Issuing an invalid query to the server will result in a parse error if the query is syntactically invalid. ArangoDB will detect such errors during query inspection and abort further processing. Instead, the error number and an error message are returned so that the errors can be fixed.
If a query passes the parsing stage, all collections referenced in the query will be opened. If any of the referenced collections is not present, query execution will again be aborted and an appropriate error message will be returned.
Under some circumstances, executing a query might also produce run-time errors that cannot be predicted from inspecting the query text alone. This is because queries might use data from collections that might also be inhomogeneous. Some examples that will cause run-time errors are:
- Division by zero: Will be triggered when an attempt is made to use the value
0
as the divisor in an arithmetic division or modulus operation - Invalid operands for arithmetic operations: Will be triggered when an attempt is made to use any non-numeric values as operands in arithmetic operations. This includes unary (unary minus, unary plus) and binary operations (plus, minus, multiplication, division, and modulus)
- Invalid operands for logical operations: Will be triggered when an attempt is made to use any non-boolean values as operand(s) in logical operations. This includes unary (logical not/negation), binary (logical and, logical or), and the ternary operators
Please refer to the @ref ArangoErrors page for a list of error codes and meanings.
Language basics
Whitespace
Whitespace can be used in the query text to increase its readability. However, for the parser any whitespace (spaces, carriage returns, line feeds, and tab stops) does not have any special meaning except that it separates individual tokens in the query. Whitespace within strings or names must be enclosed in quotes in order to be preserved.
Comments
Comments can be embedded at any position in a query. The text contained in the comment is ignored by the AQL parser. Comments cannot be nested, meaning the comment text may not contain another comment.
AQL supports two types of comments:
- Single line comments: These start with a double forward slash and end at the end of the line, or the end of the query string (whichever is first).
- Multi line comments: These start with a forward slash and asterisk, and end with an asterisk and a following forward slash. They can span as many lines as necessary.
@code /* this is a comment */ RETURN 1
/* these / RETURN / are / 1 / multiple / + / comments */ 1
/* this is a multi line comment */
// a single line comment @endcode
Keywords
On the top level, AQL offers the following operations:
- FOR: list iteration
- RETURN: results projection
- FILTER: results filtering
- SORT: result sorting
- LIMIT: result slicing
- LET: variable assignment
- COLLECT: result grouping
Each of the above operations can be initiated in a query by using a keyword of the same name. An AQL query can (and typically does) consist of multiple of the above operations.
An example AQL query might look like this:
FOR u IN users
FILTER u.type == "newbie" && u.active == true
RETURN u.name
In this example query, the terms FOR
, FILTER
, and RETURN
initiate the
higher-level operation according to their name. These terms are also keywords,
meaning that they have a special meaning in the language.
For example, the query parser will use the keywords to find out which high-level operations to execute. That also means keywords can only be used at certain locations in a query. This also makes all keywords reserved words that must not be used for other purposes than they are intended for.
For example, it is not possible to use a keyword as a collection or attribute name. If a collection or attribute need to have the same name as a keyword, the collection or attribute name needs to be quoted.
Keywords are case-insensitive, meaning they can be specified in lower, upper, or mixed case in queries. In this documentation, all keywords are written in upper case to make them distinguishable from other query parts.
In addition to the higher-level operations keywords, there are other keywords. The current list of keywords is:
- FOR
- RETURN
- FILTER
- SORT
- LIMIT
- LET
- COLLECT
- ASC
- DESC
- IN
- INTO
- NULL
- TRUE
- FALSE
Additional keywords might be added in future versions of ArangoDB.
Names
In general, names are used to identify objects (collections, attributes, variables, and functions) in AQL queries.
The maximum supported length of any name is 64 bytes. Names in AQL are always case-sensitive.
Keywords must not be used as names. If a reserved keyword should be used as a name, the name must be enclosed in backticks. Enclosing a name in backticks allows using otherwise-reserved keywords as names. An example for this is:
FOR f IN `filter`
RETURN f.`sort`
Due to the backticks, filter
and sort
are interpreted as names and not as
keywords here.
@subsubsection AqlCollectionNames Collection names
Collection names can be used in queries as they are. If a collection happens to have the same name as a keyword, the name must be enclosed in backticks.
Please refer to the @ref NamingConventions about collection name naming conventions.
@subsubsection AqlAttributeNames Attribute names
When referring to attributes of documents from a collection, the fully qualified attribute name must be used. This is because multiple collections with ambiguous attribute names might be used in a query. To avoid any ambiguity, it is not allowed to refer to an unqualified attribute name.
Please refer to the @ref NamingConventions for more information about the attribute naming conventions.
FOR u IN users
FOR f IN friends
FILTER u.active == true && f.active == true && u.id == f.userId
RETURN u.name
In the above example, the attribute names active
, name
, id
, and userId
are qualified using the collection names they belong to (u
and f
respectively).
@subsubsection AqlVariableNames Variable names
AQL offers the user to assign values to additional variables in a query. All variables that are assigned a value must have a name that is unique within the context of the query. Variable names must be different from the names of any collection name used in the same query.
FOR u IN users
LET friends = u.friends
RETURN { "name" : u.name, "friends" : friends }
In the above query, users
is a collection name, and both u
and friends
are
variable names. This is because the FOR
and LET
operations need target
variables to store their intermediate results.
Allowed characters in variable names are the letters a
to z
(both in lower
and upper case), the numbers 0
to 9
and the underscore (_
) symbol. A
variable name must not start with a number. If a variable name starts with the
underscore character, it must also contain at least one letter (a-z or A-Z).
Data types
AQL supports both primitive and compound data types. The following types are available:
- Primitive types: Consisting of exactly one value
- null: An empty value, also: The absence of a value
- bool: Boolean truth value with possible values
false
andtrue
- number: Signed (real) number
- string: UTF-8 encoded text value
- Compound types: Consisting of multiple values
- list: Sequence of values, referred to by their positions
- document: Sequence of values, referred to by their names
@subsubsection AqlLiteralsNumber Numeric literals
Numeric literals can be integers or real values. They can optionally be signed
using the +
or -
symbols. The scientific notation is also supported.
1
42
-1
-42
1.23
-99.99
0.1
-4.87e103
All numeric values are treated as 64-bit double-precision values internally. The internal format used is IEEE 754.
@subsubsection AqlLiteralsString String literals
String literals must be enclosed in single or double quotes. If the used quote character is to be used itself within the string literal, it must be escaped using the backslash symbol. Backslash literals themselves also be escaped using a backslash.
"yikes!"
"don't know"
"this is a \"quoted\" word"
"this is a longer string."
"the path separator on Windows is \\"
'yikes!'
'don\'t know'
'this is a longer string."
'the path separator on Windows is \\'
All string literals must be UTF-8 encoded. It is currently not possible to use arbitrary binary data if it is not UTF-8 encoded. A workaround to use binary data is to encode the data using base64 or other algorithms on the application side before storing, and decoding it on application side after retrieval.
@subsubsection AqlCompoundLists Lists
AQL supports two compound types:
- lists: A composition of unnamed values, each accessible by their positions
- documents: A composition of named values, each accessible by their names
The first supported compound type is the list type. Lists are effectively sequences of (unnamed/anonymous) values. Individual list elements can be accessed by their positions. The order of elements in a list is important.
A list-declaration
starts with the [
symbol and ends with the ]
symbol. A
list-declaration
contains zero or many expression
s, separated from each
other with the ,
symbol.
In the easiest case, a list is empty and thus looks like:
[ ]
List elements can be any legal expression
values. Nesting of lists is
supported.
[ 1, 2, 3 ]
[ -99, "yikes!", [ true, [ "no"], [ ] ], 1 ]
[ [ "fox", "marshal" ] ]
Individual list values can later be accesses by their positions using the []
accessor. The position of the accessed element must be a numeric
value. Positions start at 0. It is also possible to use negative index values
to access list values starting from the end of the list. This is convenient if
the length of the list is unknown and access to elements at the end of the list
is required.
// access 1st list element (element start at index 0)
u.friends[0]
// access 3rd list element
u.friends[2]
// access last list element
u.friends[-1]
// access second last list element
u.friends[-2]
@subsubsection AqlCompoundDocuments Documents
The other supported compound type is the document type. Documents are a composition of zero to many attributes. Each attribute is a name/value pair. Document attributes can be accessed individually by their names.
Document declarations start with the {
symbol and end with the }
symbol. A
document contains zero to many attribute declarations, separated from each other
with the ,
symbol. In the simplest case, a document is empty. Its
declaration would then be:
{ }
Each attribute in a document is a name/value pair. Name and value of an
attribute are separated using the :
symbol.
The attribute name is mandatory and must be specified as a quoted or unquoted string. If a keyword is to be used as an attribute name, the name must be quoted.
Any valid expression can be used as an attribute value. That also means nested documents can be used as attribute values
{ name : "Peter" }
{ "name" : "Vanessa", "age" : 15 }
{ "name" : "John", likes : [ "Swimming", "Skiing" ], "address" : { "street" : "Cucumber lane", "zip" : "94242" } }
Individual document attributes can later be accesses by their names using the
.
accessor. If a non-existing attribute is accessed, the result is null
.
u.address.city.name
u.friends[0].name.first
Bind parameters
AQL supports the usage of bind parameters, thus allowing to separate the query text from literal values used in the query. It is good practice to separate the query text from the literal values because this will prevent (malicious) injection of keywords and other collection names into an existing query. This injection would be dangerous because it might change the meaning of an existing query.
Using bind parameters, the meaning of an existing query cannot be changed. Bind parameters can be used everywhere in a query where literals can be used.
The syntax for bind parameters is @nameparameter
where nameparameter
is the
actual parameter name. The bind parameter values need to be passed along with
the query when it is executed, but not as part of the query text itself. Please
refer to the @ref HttpCursorHttp manual section for information about how to
pass the bind parameter values to the server.
FOR u IN users
FILTER u.id == @id && u.name == @nameparameter
RETURN u
Bind parameter names must start with any of the letters a
to z
(both in
lower and upper case) or a digit (0
to 9
), and can be followed by any
letter, digit or the underscore symbol.
A special type of bind parameter exists for injecting collection names. This
type of bind parameter has a name prefixed with an additional @
symbol (thus
when using the bind parameter in a query, two @
symbols must be used).
FOR u IN @@collection
FILTER u.active == true
RETURN u
Type and value order
When checking for equality or inequality or when determining the sort order of values, AQL uses a deterministic algorithm that takes both the data types and the actual values into account.
The compared operands are first compared by their data types, and only by their data values if the operands have the same data types.
The following type order is used when comparing data types:
null < bool < number < string < list < document
This means null
is the smallest type in AQL and document
is the type with
the highest order. If the compared operands have a different type, then the
comparison result is determined and the comparison is finished.
For example, the boolean true
value will always be less than any numeric or
string value, any list (even an empty list) or any document. Additionally, any
string value (even an empty string) will always be greater than any numeric
value, a boolean value, true
or false
.
null < false
null < true
null < 0
null < ''
null < ' '
null < '0'
null < 'abc'
null < [ ]
null < { }
false < true
false < 0
false < ''
false < ' '
false < '0'
false < 'abc'
false < [ ]
false < { }
true < 0
true < ''
true < ' '
true < '0'
true < 'abc'
true < [ ]
true < { }
0 < ''
0 < ' '
0 < '0'
0 < 'abc'
0 < [ ]
0 < { }
'' < ' '
'' < '0'
'' < 'abc'
'' < [ ]
'' < { }
[ ] < { }
If the two compared operands have the same data types, then the operands values are compared. For the primitive types (null, boolean, number, and string), the result is defined as follows:
- null:
null
is equal tonull
- boolean:
false
is less thantrue
- number: numeric values are ordered by their cardinal value
- string: string values are ordered using a localized comparison, see @ref CommandLineDefaultLanguage "--default-language"
Note: unlike in SQL, null
can be compared to any value, including null
itself, without the result being converted into null
automatically.
For compound, types the following special rules are applied:
Two list values are compared by comparing their individual elements position by
position, starting at the first element. For each position, the element types
are compared first. If the types are not equal, the comparison result is
determined, and the comparison is finished. If the types are equal, then the
values of the two elements are compared. If one of the lists is finished and
the other list still has an element at a compared position, then null
will be
used as the element value of the fully traversed list.
If a list element is itself a compound value (a list or a document), then the comparison algorithm will check the element's sub values recursively. The element's sub elements are compared recursively.
[ ] < [ 0 ]
[ 1 ] < [ 2 ]
[ 1, 2 ] < [ 2 ]
[ 99, 99 ] < [ 100 ]
[ false ] < [ true ]
[ false, 1 ] < [ false, '' ]
Two documents operands are compared by checking attribute names and value. The attribute names are compared first. Before attribute names are compared, a combined list of all attribute names from both operands is created and sorted lexicographically. This means that the order in which attributes are declared in a document is not relevant when comparing two documents.
The combined and sorted list of attribute names is then traversed, and the
respective attributes from the two compared operands are then looked up. If one
of the documents does not have an attribute with the sought name, its attribute
value is considered to be null
. Finally, the attribute value of both
documents is compared using the before mentioned data type and value comparison.
The comparisons are performed for all document attributes until there is an
unambiguous comparison result. If an unambiguous comparison result is found, the
comparison is finished. If there is no unambiguous comparison result, the two
compared documents are considered equal.
{ } < { "a" : 1 }
{ } < { "a" : null }
{ "a" : 1 } < { "a" : 2 }
{ "b" : 1 } < { "a" : 0 }
{ "a" : { "c" : true } } < { "a" : { "c" : 0 } }
{ "a" : { "c" : true, "a" : 0 } } < { "a" : { "c" : false, "a" : 1 } }
{ "a" : 1, "b" : 2 } == { "b" : 2, "a" : 1 }
Accessing data from collections
Collection data can be accessed by specifying a collection name in a query. A
collection can be understood as a list of documents, and that is how they are
treated in AQL. Documents from collections are normally accessing using the
FOR
keyword. Note that when iterating over documents from a collection, the
order of documents is undefined. To traverse documents in an explicit and
deterministic order, the SORT
keyword should be used in addition.
Data in collections is stored in documents, with each document potentially having different attributes than other documents. This is true even for documents of the same collection.
It is therefore quite normal to encounter documents that do not have some or all
of the attributes that are queried in an AQL query. In this case, the
non-existing attributes in the document will be treated as if they would exist
with a value of null
. That means that comparing a document attribute to
null
will return true if the document has the particular attribute and the
attribute has a value of null
, or that the document does not have the
particular attribute at all.
For example, the following query will return all documents from the collection
users
that have a value of null
in the attribute name
, plus all documents
from users
that do not have the name
attribute at all:
FOR u IN users
FILTER u.name == null
RETURN u
Furthermore, null
is less than any other value (excluding null
itself). That
means documents with non-existing attributes might be included in the result
when comparing attribute values with the less than or less equal operators.
For example, the following query will return all documents from the collection
users
that have an attribute age
with a value less than 39
, but also all
documents from the collection that do not have the attribute age
at all.
FOR u IN users
FILTER u.age < 39
RETURN u
This behavior should always be taken into account when writing queries.
Operators
AQL supports a number of operators that can be used in expressions. There are comparison, logical, arithmetic, and the ternary operator.
@subsubsection AqlOperatorsComparison Comparison operators
Comparison (or relational) operators compare two operands. They can be used with any input data types, and will return a boolean result value.
The following comparison operators are supported:
==
equality!=
inequality<
less than<=
less or equal>
greater than>=
greater or equalin
test if a value is contained in a list
The in
operator expects the second operand to be of type list. All other
operators accept any data types for the first and second operands.
Each of the comparison operators returns a boolean value if the comparison can
be evaluated and returns true
if the comparison evaluates to true, and false
otherwise.
Some examples for comparison operations in AQL:
1 > 0
true != null
45 <= "yikes!"
65 != "65"
65 == 65
1.23 < 1.32
1.5 IN [ 2, 3, 1.5 ]
@subsubsection AqlOperatorsLogical Logical operators
Logical operators combine two boolean operands in a logical operation and return a boolean result value.
The following logical operators are supported:
&&
logical and operator||
logical or operator!
logical not/negation operator
Some examples for logical operations in AQL:
u.age > 15 && u.address.city != ""
true || false
!u.isInvalid
The &&
, ||
, and !
operators expect their input operands to be boolean
values each. If a non-boolean operand is used, the operation will fail with an
error. In case all operands are valid, the result of each logical operator is a
boolean value.
Both the &&
and ||
operators use short-circuit evaluation and only evaluate
the second operand if the result of the operation cannot be determined by
checking the first operand alone.
@subsubsection AqlOperatorsArithmetic Arithmetic operators
Arithmetic operators perform an arithmetic operation on two numeric operands. The result of an arithmetic operation is again a numeric value. Operators are supported.
AQL supports the following arithmetic operators:
+
addition-
subtraction*
multiplication/
division%
modulus
These operators work with numeric operands only. Invoking any of the operators with non-numeric operands will result in an error. An error will also be raised for some other edge cases as division by zero, numeric over- or underflow etc. If both operands are numeric and the computation result is also valid, the result will be returned as a numeric value.
The unary plus and unary minus are supported as well.
Some example arithmetic operations:
1 + 1
33 - 99
12.4 * 4.5
13.0 / 0.1
23 % 7
-15
+9.99
@subsubsection AQLOperatorTernary Ternary operator
AQL also supports a ternary operator that can be used for conditional evaluation. The ternary operator expects a boolean condition as its first operand, and it returns the result of the second operand if the condition evaluates to true, and the third operand otherwise.
Example:
u.age > 15 || u.active == true ? u.userId : null
@subsubsection AQLOperatorRange Range operator
AQL supports expressing simple numeric ranges with the ..
operator.
This operator can be used to easily iterate over a sequence of numeric
values.
The ..
operator will produce a list of values in the defined range, with
both bounding values included.
Example:
2010..2013
will produce the following result:
[ 2010, 2011, 2012, 2013 ]
@subsubsection AQLOperatorsPrecedence Operator precedence
The operator precedence in AQL is as follows (lowest precedence first):
? :
ternary operator||
logical or&&
logical and==
,!=
equality and inequalityin
in operator<
,<=
,>=
,>
less than, less equal, greater equal, greater than+
,-
addition, subtraction*
,/
,%
multiplication, division, modulus!
,+
,-
logical negation, unary plus, unary minus[*]
expansion()
function call.
member access[]
indexed value access
The parentheses (
and )
can be used to enforce a different operator
evaluation order.
Functions
AQL supports functions to allow more complex computations. Functions can be called at any query position where an expression is allowed. The general function call syntax is:
FUNCTIONNAME(arguments)
where FUNCTIONNAME
is the name of the function to be called, and arguments
is a comma-separated list of function arguments. If a function does not need any
arguments, the argument list can be left empty. However, even if the argument
list is empty the parentheses around it are still mandatory to make function
calls distinguishable from variable names.
Some example function calls:
HAS(user, "name")
LENGTH(friends)
COLLECTIONS()
In contrast to collection and variable names, function names are case-insensitive,
i.e. LENGTH(foo)
and length(foo)
are equivalent.
@subsubsection AqlFunctionsExtending Extending AQL
Since ArangoDB 1.3, it is possible to extend AQL with user-defined functions. These functions need to be written in Javascript, and be registered before usage in a query.
Please refer to @ref ExtendingAql for more details on this.
By default, any function used in an AQL query will be sought in the built-in
function namespace _aql
. This is the default namespace that contains all AQL
functions that are shipped with ArangoDB.
To refer to a user-defined AQL function, the function name must be fully qualified
to also include the user-defined namespace. The ::
symbol is used as the namespace
separator:
MYGROUP::MYFUNC()
MYFUNCTIONS::MATH::RANDOM()
As all AQL function names, user function names are also case-insensitive.
@subsubsection AqlFunctionsCasting Type cast functions
As mentioned before, some of the operators expect their operands to have a certain data type. For example, the logical operators expect their operands to be boolean values, and the arithmetic operators expect their operands to be numeric values. If an operation is performed with operands of an unexpected type, the operation will fail with an error. To avoid such failures, value types can be converted explicitly in a query. This is called type casting.
In an AQL query, type casts are performed only upon request and not implicitly.
This helps avoiding unexpected results. All type casts have to be performed by
invoking a type cast function. AQL offers several type cast functions for this
task. Each of the these functions takes an operand of any data type and returns
a result value of type corresponding to the function name (e.g. TO_NUMBER()
will return a number value):
-
@FN{TO_BOOL(@FA{value})}: Takes an input @FA{value} of any type and converts it into the appropriate boolean value as follows:
null
is converted tofalse
.- Numbers are converted to
true
if they are unequal to 0, and tofalse
otherwise. - Strings are converted to
true
if they are non-empty, and tofalse
otherwise. - Lists are converted to
true
if they are non-empty, and tofalse
otherwise. - Documents are converted to
true
if they are non-empty, and tofalse
otherwise.
-
@FN{TO_NUMBER(@FA{value})}: Takes an input @FA{value} of any type and converts it into a numeric value as follows:
null
,false
, lists, and documents are converted to the value0
.true
is converted to1
.- Strings are converted to their numeric equivalent if the full string content is
is a valid number, and to
0
otherwise.
-
@FN{TO_STRING(@FA{value})}: Takes an input @FA{value} of any type and converts it into a string value as follows:
null
is converted to the string"null"
false
is converted to the string"false"
,true
to the string"true"
- Numbers, lists and documents are converted to their string equivalents.
-
@FN{TO_LIST(@FA{value})}: Takes an input @FA{value} of any type and converts it into a list value as follows:
null
is converted to an empty list- Boolean values, numbers and strings are converted to a list containing the original value as its single element
- Documents are converted to a list containing their attribute values as list elements
@subsubsection AqlFunctionsChecking Type check functions
AQL also offers functions to check the data type of a value at runtime. The following type check functions are available. Each of these functions takes an argument of any data type and returns true if the value has the type that is checked for, and false otherwise.
The following type check functions are available:
-
@FN{IS_NULL(@FA{value})}: Checks whether @FA{value} is a
null
value -
@FN{IS_BOOL(@FA{value})}: Checks whether @FA{value} is a boolean value
-
@FN{IS_NUMBER(@FA{value})}: Checks whether @FA{value} is a numeric value
-
@FN{IS_STRING(@FA{value})}: Checks whether @FA{value} is a string value
-
@FN{IS_LIST(@FA{value})}: Checks whether @FA{value} is a list value
-
@FN{IS_DOCUMENT(@FA{value})}: Checks whether @FA{value} is a document value
@subsubsection AqlFunctionsString String functions
For string processing, AQL offers the following functions:
-
@FN{CONCAT(@FA{value1}, @FA{value2}, ... @FA{valuen})}: Concatenate the strings passed as in @FA{value1} to @FA{valuen}.
null
values are ignored -
@FN{CONCAT_SEPARATOR(@FA{separator}, @FA{value1}, @FA{value2}, ... @FA{valuen})}: Concatenate the strings passed as arguments @FA{value1} to @FA{valuen} using the @FA{separator} string.
null
values are ignored -
@FN{LENGTH(@FA{value})}: Return the number of characters in @FA{value}.
-
@FN{CHAR_LENGTH(@FA{value})}: Return the number of characters in @FA{value}. This is a synonym for @FN{LENGTH(@FA{value})}
-
@FN{LOWER(@FA{value})}: Lower-case @FA{value}
-
@FN{UPPER(@FA{value})}: Upper-case @FA{value}
-
@FN{SUBSTRING(@FA{value}, @FA{offset}, @FA{length})}: Return a substring of @FA{value}, starting at @FA{offset} and with a maximum length of @FA{length} characters. Offsets start at position 0
-
@FN{LEFT(@FA{value}, @FA{LENGTH})}: Returns the @FA{LENGTH} leftmost characters of the string @FA{VALUE}
-
@FN{RIGHT(@FA{value}, @FA{LENGTH})}: Returns the @FA{LENGTH} rightmost characters of the string @FA{VALUE}
-
@FN{TRIM(@FA{value}, @FA{type})}: Returns the string @FA{VALUE} with whitespace stripped from the start and/or end. The optional @FA{type} parameter specifies from which parts of the string the whitespace is stripped:
- @FA{type} 0 will strip whitespace from the start and end of the string
- @FA{type} 1 will strip whitespace from the start of the string only
- @FA{type} 2 will strip whitespace from the end of the string only
-
@FN{REVERSE(@FA{value})}: Returns the reverse of the string @FA{value}
-
@FN{CONTAINS(@FA{text}, @FA{search}, @FA{return-index})}: Checks whether the string @FA{search} is contained in the string @FA{text}. By default, this function returns
true
if @FA{search} is contained in @FA{text}, andfalse
otherwise. By passingtrue
as the third function parameter @FA{return-index}, the function will return the position of the first occurence of @FA{search} within @FA{text}, starting at offset 0, or-1
if @FA{search} is not contained in @FA{text}.The string matching performed by @FN{CONTAINS} is case-sensitive.
-
@FN{LIKE(@FA{text}, @FA{search}, @FA{case-insensitive})}: Checks whether the pattern @FA{search} is contained in the string @FA{text}, using wildcard matching. Returns
true
if the pattern is contained in @FA{text}, andfalse
otherwise. The @FA{pattern} string can contain the wildcard characters%
(meaning any sequence of characters) and_
(any single character).The string matching performed by @FN{LIKE} is case-sensitive by default, but by passing
true
as the third parameter, the matching will be case-insensitive.The value for @FA{search} cannot be a variable or a document attribute. The actual value must be present at query parse time already.
@subsubsection AqlFunctionsNumeric Numeric functions
AQL offers some numeric functions for calculations. The following functions are supported:
-
@FN{FLOOR(@FA{value})}: Returns the integer closest but not greater to @FA{value}
-
@FN{CEIL(@FA{value})}: Returns the integer closest but not less than @FA{value}
-
@FN{ROUND(@FA{value})}: Returns the integer closest to @FA{value}
-
@FN{ABS(@FA{value})}: Returns the absolute part of @FA{value}
-
@FN{SQRT(@FA{value})}: Returns the square root of @FA{value}
-
@FN{RAND()}: Returns a pseudo-random number between 0 and 1
@subsubsection AqlFunctionsDate Date functions
AQL offers functionality to work with dates. Dates are no datatypes of their own in AQL (neither they are in JSON, which is often used as a format to ship data into and out of ArangoDB). Instead, dates in AQL are internally represented by either numbers (timestamps) or strings. The date functions in AQL provide mechanisms to convert from a numeric timestamp to a string representation and vice versa.
There are two date functions in AQL to create dates for further use:
-
@FN{DATE_TIMESTAMP(@FA{date})}: Creates a UTC timestamp value from @FA{date}.
-
@FN{DATE_TIMESTAMP(@FA{year}, @FA{month}, @FA{day}, @FA{hour}, @FA{minute}, @FA{second}, @FA{millisecond})}: Same as before, but allows specifying the individual date components separately. All parameters after @FA{day} are optional.
-
@FN{DATE_ISO8601(@FA{date})}: Returns an ISO8601 datetime string from @FA{date}. The datetime string will always use UTC time, indicated by the
Z
at its end. -
@FN{DATE_ISO8601(@FA{year}, @FA{month}, @FA{day}, @FA{hour}, @FA{minute}, @FA{second}, @FA{millisecond})}: same as before, but allows specifying the individual date components separately. All parameters after @FA{day} are optional.
These two above date functions accept the following input values:
-
numeric timestamps, indicating the number of milliseconds elapsed since the UNIX epoch (i.e. January 1st 1970 00:00:00 UTC). An example timestamp value is
1399472349522
, which translates to2014-05-07T14:19:09.522Z
. -
datetime strings in formats @LIT{YYYY-MM-DDTHH:MM:SS.MMM}, @LIT{YYYY-MM-DD HH:MM:SS.MMM}, or @LIT{YYYY-MM-DD}. Milliseconds are always optional. A timezone difference may optionally be added at the end of the string, with the hours and minutes that need to be added or subtracted to the datetime value. For example,
2014-05-07T14:19:09+01:00
can be used to specify a one hour offset, and2014-05-07T14:19:09+07:30
can be specified for seven and half hours offset. Negative offsets are also possible. Alternatively to an offset, aZ
can be used to indicate UTC / Zulu time.An example value is
2014-05-07T14:19:09.522Z
meaning May 7th 2014, 14:19:09 and 522 milliseconds, UTC / Zulu time. Another example value without time component is2014-05-07Z
.Please note that if no timezone offset is specified in a datestring, ArangoDB will assume UTC time automatically. This is done to ensure portability of queries across servers with different timezone settings, and because timestamps will always be UTC-based.
-
individual date components as separate function arguments, in the following order:
- year
- month
- day
- hour
- minute
- second
- millisecond
All components following
day
are optional and can be omitted. Note that no timezone offsets can be specified when using separate date components, and UTC / Zulu time will be used.
The following calls to DATE_TIMESTAMP
are equivalent and will all return
1399472349522
:
DATE_TIMESTAMP("2014-05-07T14:19:09.522")
DATE_TIMESTAMP("2014-05-07T14:19:09.522Z")
DATE_TIMESTAMP("2014-05-07 14:19:09.522")
DATE_TIMESTAMP("2014-05-07 14:19:09.522Z")
DATE_TIMESTAMP(2014, 5, 7, 14, 19, 9, 522)
DATE_TIMESTAMP(1399472349522)
The same is true for calls to DATE_ISO8601
that also accepts variable input
formats:
DATE_ISO8601("2014-05-07T14:19:09.522Z")
DATE_ISO8601("2014-05-07 14:19:09.522Z")
DATE_ISO8601(2014, 5, 7, 14, 19, 9, 522)
DATE_ISO8601(1399472349522)
The above functions are all equivalent and will return "2014-05-07T14:19:09.522Z"
.
The following date functions can be used with dates created by DATE_TIMESTAMP
and
DATE_ISO8601
:
-
@FN{DATE_DAYOFWEEK(@FA{date})}: Returns the weekday number of @FA{date}. The return values have the following meanings:
- 0: Sunday
- 1: Monday
- 2: Tuesday
- 3: Wednesday
- 4: Thursday
- 5: Friday
- 6: Saturday
-
@FN{DATE_YEAR(@FA{date})}: Returns the year part of @FA{date} as a number.
-
@FN{DATE_MONTH(@FA{date})}: Returns the month part of @FA{date} as a number.
-
@FN{DATE_DAY(@FA{date})}: Returns the day part of @FA{date} as a number.
-
@FN{DATE_HOUR(@FA{date})}: Returns the hour part of @FA{date} as a number.
-
@FN{DATE_MINUTE(@FA{date})}: Returns the minute part of @FA{date} as a number.
-
@FN{DATE_SECOND(@FA{date})}: Returns the seconds part of @FA{date} as a number.
-
@FN{DATE_MILLISECOND(@FA{date})}: Returns the milliseconds part of @FA{date} as a number.
The following other date functions are also available:
-
@FN{DATE_NOW()}: Returns the current time as a timestamp.
Note that this function is evaluated on every invocation and may return different values when invoked multiple times in the same query.
@subsubsection AqlFunctionsList List functions
AQL supports the following functions to operate on list values:
-
@FN{LENGTH(@FA{list})}: Returns the length (number of list elements) of @FA{list}. If @FA{list} is a document, returns the number of attribute keys of the document, regardless of their values.
-
@FN{FLATTEN(@FA{list}, @FA{depth})}: Turns a list of lists into a flat list. All list elements in @FA{list} will be expanded in the result list. Non-list elements are added as they are. The function will recurse into sub-lists up to a depth of @FA{depth}. @FA{depth} has a default value of 1.
Example:
FLATTEN([ 1, 2, [ 3, 4 ], 5, [ 6, 7 ], [ 8, [ 9, 10 ] ])
will produce:
[ 1, 2, 3, 4, 5, 6, 7, 8, [ 9, 10 ] ]
To fully flatten the list, use a @FA{depth} of 2:
FLATTEN([ 1, 2, [ 3, 4 ], 5, [ 6, 7 ], [ 8, [ 9, 10 ] ], 2)
This will produce:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
-
@FN{MIN(@FA{list})}: Returns the smallest element of @FA{list}.
null
values are ignored. If the list is empty or onlynull
values are contained in the list, the function will returnnull
. -
@FN{MAX(@FA{list})}: Returns the greatest element of @FA{list}.
null
values are ignored. If the list is empty or onlynull
values are contained in the list, the function will returnnull
. -
@FN{AVERAGE(@FA{list})}: Returns the average (arithmetic mean) of the values in @FA{list}. This requires the elements in @FA{list} to be numbers.
null
values are ignored. If the list is empty or onlynull
values are contained in the list, the function will returnnull
. -
@FN{SUM(@FA{list})}: Returns the sum of the values in @FA{list}. This requires the elements in @FA{list} to be numbers.
null
values are ignored. -
@FN{MEDIAN(@FA{list})}: Returns the median value of the values in @FA{list}. This requires the elements in @FA{list} to be numbers.
null
values are ignored. If the list is empty or onlynull
values are contained in the list, the function will returnnull
. -
@FN{VARIANCE_POPULATION(@FA{list})}: Returns the population variance of the values in @FA{list}. This requires the elements in @FA{list} to be numbers.
null
values are ignored. If the list is empty or onlynull
values are contained in the list, the function will returnnull
. -
@FN{VARIANCE_SAMPLE(@FA{list})}: Returns the sample variance of the values in @FA{list}. This requires the elements in @FA{list} to be numbers.
null
values are ignored. If the list is empty or onlynull
values are contained in the list, the function will returnnull
. -
@FN{STDDEV_POPULATION(@FA{list})}: Returns the population standard deviation of the values in @FA{list}. This requires the elements in @FA{list} to be numbers.
null
values are ignored. If the list is empty or onlynull
values are contained in the list, the function will returnnull
. -
@FN{STDDEV_SAMPLE(@FA{list})}: Returns the sample standard deviation of the values in @FA{list}. This requires the elements in @FA{list} to be numbers.
null
values are ignored. If the list is empty or onlynull
values are contained in the list, the function will returnnull
. -
@FN{REVERSE(@FA{list})}: Returns the elements in @FA{list} in reversed order.
-
@FN{FIRST(@FA{list})}: Returns the first element in @FA{list} or
null
if the list is empty. -
@FN{LAST(@FA{list})}: Returns the last element in @FA{list} or
null
if the list is empty. -
@FN{NTH(@FA{list}, @FA{position})}: Returns the list element at position @FA{position}. Positions start at 0. If @FA{position} is negative or beyond the upper bound of the list specified by @FA{list}, then
null
will be returned. -
@FN{POSITION(@FA{list}, @FA{search}, @FA{return-index})}: Returns the position of the element @FA{search} in list @FA{list}. Positions start at 0. If the element is not found, then
-1
is returned. If @FA{return-index} isfalse
, then instead of the position onlytrue
orfalse
are returned, depending on whether the sought element is contained in the list. -
@FN{SLICE(@FA{list}, @FA{start}, @FA{length})}: Extracts a slice of the list specified by @FA{list}. The extraction will start at list element with position @FA{start}. Positions start at 0. Up to @FA{length} elements will be extracted. If @FA{length} is not specified, all list elements starting at @FA{start} will be returned. If @FA{start} is negative, it can be used to indicate positions from the end of the list.
Examples:
SLICE([ 1, 2, 3, 4, 5 ], 0, 1)
will return
[ 1 ]
SLICE([ 1, 2, 3, 4, 5 ], 1, 2)
will return
[ 2, 3 ]
SLICE([ 1, 2, 3, 4, 5 ], 3)
will return
[ 4, 5 ]
SLICE([ 1, 2, 3, 4, 5 ], 1, -1)
will return
[ 2, 3, 4 ]
SLICE([ 1, 2, 3, 4, 5 ], 0, -2)
will return
[ 1, 2, 3 ]
-
@FN{UNIQUE(@FA{list})}: Returns all unique elements in @FA{list}. To determine uniqueness, the function will use the comparison order defined in @ref AqlTypeOrder. Calling this function might return the unique elements in any order.
-
@FN{UNION(@FA{list1, list2, ...})}: Returns the union of all lists specified. The function expects at least two list values as its arguments. The result is a list of values in an undefined order.
Note: No duplicates will be removed. In order to remove duplicates, please use either @LIT{UNION_DISTINCT} function or apply the @LIT{UNIQUE} on the result of @LIT{union}.
Example:
RETURN UNION( [ 1, 2, 3 ], [ 1, 2 ] )
will produce:
[ [ 1, 2, 3, 1, 2 ] ]
with duplicate removal:
RETURN UNIQUE( UNION( [ 1, 2, 3 ], [ 1, 2 ] ) )
will produce:
[ [ 1, 2, 3 ] ]
-
@FN{UNION_DISTINCT(@FA{list1, list2, ...})}: Returns the union of distinct values of all lists specified. The function expects at least two list values as its arguments. The result is a list of values in an undefined order.
-
@FN{MINUS(@FA{list1, list2, ...})}: Returns the difference of all lists specified. The function expects at least two list values as its arguments. The result is a list of values that occur in the first list but not in any of the subsequent lists. The order of the result list is undefined and should not be relied on. Note: duplicates will be removed.
-
@FN{INTERSECTION(@FA{list1, list2, ...})}: Returns the intersection of all lists specified. The function expects at least two list values as its arguments. The result is a list of values that occur in all arguments. The order of the result list is undefined and should not be relied on.
Note: Duplicates will be removed.
Apart from these functions, AQL also offers several language constructs (e.g.
FOR
, SORT
, LIMIT
, COLLECT
) to operate on lists.
@subsubsection AqlFunctionsDocument Document functions
AQL supports the following functions to operate on document values:
-
@FN{MATCHES(@FA{document}, @FA{examples}, @FA{return-index})}: Compares the document @FA{document} against each example document provided in the list @FA{examples}. If @FA{document} matches one of the examples,
true
is returned, and if there is no matchfalse
will be returned. The default return value type can be changed by passingtrue
as the third function parameter @FA{return-index}. Setting this flag will return the index of the example that matched (starting at offset 0), or-1
if there was no match.The comparisons will be started with the first example. All attributes of the example will be compared against the attributes of @FA{document}. If all attributes match, the comparison stops and the result is returned. If there is a mismatch, the function will continue the comparison with the next example until there are no more examples left.
The @FA{examples} must be a list of 1..n example documents, with any number of attributes each. Note: specifying an empty list of examples is not allowed.
Example usage:
RETURN MATCHES( { "test" : 1 }, [ { "test" : 1, "foo" : "bar" }, { "foo" : 1 }, { "test : 1 } ], true)
This will return
2
, because the third example matches, and because thereturn-index
flag is set totrue
. -
@FN{MERGE(@FA{document1}, @FA{document2}, ... @FA{documentn})}: Merges the documents in @FA{document1} to @FA{documentn} into a single document. If document attribute keys are ambiguous, the merged result will contain the values of the documents contained later in the argument list.
For example, two documents with distinct attribute names can easily be merged into one:
RETURN MERGE( { "user1" : { "name" : "J" } }, { "user2" : { "name" : "T" } } ) [ { "user1" : { "name" : "J" },
"user2" : { "name" : "T" } } ]
When merging documents with identical attribute names, the attribute values of the latter documents will be used in the end result:
RETURN MERGE( { "users" : { "name" : "J" } }, { "users" : { "name" : "T" } } ) [ { "users" : { "name" : "T" } } ]
Please note that merging will only be done for top-level attributes. If you wish to merge sub-attributes, you should consider using
MERGE_RECURSIVE
instead. -
@FN{MERGE_RECURSIVE(@FA{document1}, @FA{document2}, ... @FA{documentn})}: Recursively merges the documents in @FA{document1} to @FA{documentn} into a single document. If document attribute keys are ambiguous, the merged result will contain the values of the documents contained later in the argument list.
For example, two documents with distinct attribute names can easily be merged into one:
RETURN MERGE_RECURSIVE( { "user-1" : { "name" : "J", "livesIn" : { "city" : "LA" } } }, { "user-1" : { "age" : 42, "livesIn" : { "state" : "CA" } } } ) [ { "user-1" : { "name" : "J", "livesIn" : { "city" : "LA", "state" : "CA" }, "age" : 42 } } ]
-
@FN{LENGTH(@FA{document})}: Return the number of attributes in document @FA{document}.
-
@FN{HAS(@FA{document}, @FA{attributename})}: Returns
true
if @FA{document} has an attribute named @FA{attributename}, andfalse
otherwise. -
@FN{ATTRIBUTES(@FA{document}, @FA{removeInternal}, @FA{sort})}: Returns the attribute names of the document @FA{document} as a list. If @FA{removeInternal} is set to
true
, then all internal attributes (such as_id
,_key
etc.) are removed from the result. If @FA{sort} is set totrue
, then the attribute names in the result will be sorted. Otherwise they will be returned in any order. -
@FN{UNSET(@FA{document}, @FA{attributename}, ...)}: Removes the attributes @FA{attributename} (can be one or many) from @FA{document}. All other attributes will be preserved. Multiple attribute names can be specified by either passing multiple individual string argument names, or by passing a list of attribute names:
RETURN UNSET(doc, '_id', '_key', [ 'foo', 'bar' ])
-
@FN{KEEP(@FA{document}, @FA{attributename}, ...)}: Keeps only the attributes @FA{attributename} (can be one or many) from @FA{document}. All other attributes will be removed from the result. Multiple attribute names can be specified by either passing multiple individual string argument names, or by passing a list of attribute names:
RETURN KEEP(doc, 'firstname', 'name', 'likes')
-
@FN{PARSE_IDENTIFIER(@FA{document-handle})}: Parses the document handle specified in @FA{document-handle} and returns a the handle's individual parts a separate attributes. This function can be used to easily determine the collection name and key from a given document. The @FA{document-handle} can either be a regular document from a collection, or a document identifier string (e.g.
_users/1234
). Passing either a non-string or a non-document or a document without an_id
attribute will result in an error.RETURN PARSE_IDENTIFIER('_users/my-user') [ { "collection" : "_users", "key" : "my-user" } ] RETURN PARSE_IDENTIFIER({ "_id" : "mycollection/mykey", "value" : "some value" }) [ { "collection" : "mycollection", "key" : "mykey" } ]
@subsubsection AqlFunctionsGeo Geo functions
AQL offers the following functions to filter data based on geo indexes:
-
@FN{NEAR(@FA{collection}, @FA{latitude}, @FA{longitude}, @FA{limit}, @FA{distancename})}: Returns at most @FA{limit} documents from collection @FA{collection} that are near @FA{latitude} and @FA{longitude}. The result contains at most @FA{limit} documents, returned in any order. If more than @FA{limit} documents qualify, it is undefined which of the qualifying documents are returned. Optionally, the distances between the specified coordinate (@FA{latitude} and @FA{longitude}) and the document coordinates can be returned as well. To make use of that, an attribute name for the distance result has to be specified in the @FA{distancename} argument. The result documents will contain the distance value in an attribute of that name. @FA{limit} is an optional parameter since ArangoDB 1.3. If it is not specified or null, a limit value of 100 will be applied.
-
@FN{WITHIN(@FA{collection}, @FA{latitude}, @FA{longitude}, @FA{radius}, @FA{distancename})}: Returns all documents from collection @FA{collection} that are within a radius of @FA{radius} around that specified coordinate (@FA{latitude} and @FA{longitude}). The order in which the result documents are returned is undefined. Optionally, the distance between the coordinate and the document coordinates can be returned as well. To make use of that, an attribute name for the distance result has to be specified in the @FA{distancename} argument. The result documents will contain the distance value in an attribute of that name.
Note: these functions require the collection @FA{collection} to have at least one geo index. If no geo index can be found, calling this function will fail with an error.
@subsubsection AqlFunctionsFulltext Fulltext functions
AQL offers the following functions to filter data based on fulltext indexes:
-
@FN{FULLTEXT(@FA{collection}, @FA{attribute}, @FA{query})}: Returns all documents from collection @FA{collection} for which the attribute @FA{attribute} matches the fulltext query @FA{query}. @FA{query} is a comma-separated list of sought words (or prefixes of sought words). To distinguish between prefix searches and complete-match searches, each word can optionally be prefixed with either the
prefix:
orcomplete:
qualifier. Different qualifiers can be mixed in the same query. Not specifying a qualifier for a search word will implicitly execute a complete-match search for the given word:-
FULLTEXT(emails, "body", "banana")
Will look for the wordbanana
in the attributebody
of the collectioncollection
. -
FULLTEXT(emails, "body", "banana,orange")
Will look for boths the wordsbanana
andorange
in the mentioned attribute. Only those documents will be returned that contain both words. -
FULLTEXT(emails, "body", "prefix:head")
Will look for documents that contain any words starting with the prefixhead
. -
FULLTEXT(emails, "body", "prefix:head,complete:aspirin")
Will look for all documents that contain a word starting with the prefixhead
and that also contain the (complete) wordaspirin
. Note: specifyingcomplete
is optional here. -
FULLTEXT(emails, "body", "prefix:cent,prefix:subst")
Will look for all documents that contain a word starting with the prefixcent
and that also contain a word starting with the prefixsubst
.
If multiple search words (or prefixes) are given, then by default the results will be AND-combined, meaning only the logical intersection of all searches will be returned. It is also possible to combine partial results with a logical OR, and with a logical NOT:
-
FULLTEXT(emails, "body", "+this,+text,+document")
Will return all documents that contain all the mentioned words. Note: specifying the+
symbols is optional here. -
FULLTEXT(emails, "body", "banana,|apple")
Will return all documents that contain either (or both) wordsbanana
orapple
. -
FULLTEXT(emails, "body", "banana,-apple")
Will return all documents that contain the wordbanana
but do not contain the wordapple
. -
FULLTEXT(emails, "body", "banana,pear,-cranberry")
Will return all documents that contain both the wordsbanana
andpear
but do not contain the wordcranberry
.
No precedence of logical operators will be honored in a fulltext query. The query will simply be evaluated from left to right.
-
Note: the FULLTEXT
function requires the collection @FA{collection} to have a
fulltext index on attribute
. If no fulltext index is available, this function
will fail with an error.
@subsubsection AqlFunctionsGraph Graph functions
AQL has the following functions to traverse graphs:
-
@FN{PATHS(@FA{vertexcollection}, @FA{edgecollection}, @FA{direction}, @FA{followcycles})}: returns a list of paths through the graph defined by the nodes in the collection @FA{vertexcollection} and edges in the collection @FA{edgecollection}. For each vertex in @FA{vertexcollection}, it will determine the paths through the graph depending on the value of @FA{direction}:
"outbound"
: Follow all paths that start at the current vertex and lead to another vertex"inbound"
: Follow all paths that lead from another vertex to the current vertex"any"
: Combination of"outbound"
and"inbound"
The default value for @FA{direction} is"outbound"
. If @FA{followcycles} is true, cyclic paths will be followed as well. This is turned off by default.
The result of the function is a list of paths. Paths of length 0 will also be returned. Each path is a document consisting of the following attributes:
vertices
: list of vertices visited along the pathedges
: list of edges visited along the path (might be empty)source
: start vertex of pathdestination
: destination vertex of path
Example calls:
PATHS(friends, friendrelations, "outbound", false) FOR p IN PATHS(friends, friendrelations, "outbound") FILTER p.source._id == "123456/123456" && LENGTH(p.edges) == 2 RETURN p.vertices[*].name
-
@FN{TRAVERSAL(@FA{vertexcollection}, @FA{edgecollection}, @FA{startVertex}, @FA{direction}, @FA{options})}: Traverses the graph described by @FA{vertexcollection} and @FA{edgecollection}, starting at the vertex identified by id @FA{startVertex}. Vertex connectivity is specified by the @FA{direction} parameter:
"outbound"
: Vertices are connected in_from
to_to
order"inbound"
: Vertices are connected in_to
to_from
order"any"
: Vertices are connected in both_to
to_from
and in_from
to_to
order
Additional options for the traversal can be provided via the @FA{options} document:
-
strategy
: Defines the traversal strategy. Possible values aredepthfirst
andbreadthfirst
. Defaults todepthfirst
-
order
: Defines the traversal order: Possible values arepreorder
andpostorder
. Defaults topreorder
-
itemOrder
: Defines the level item order. Can beforward
orbackward
. Defaults toforward
-
minDepth
: Minimum path depths for vertices to be included. This can be used to include only vertices in the result that are found after a certain minimum depth. Defaults to 0 -
maxIterations
: Maximum number of iterations in each traversal. This number can be set to prevent endless loops in traversal of cyclic graphs. When a traversal performs as many iterations as themaxIterations
value, the traversal will abort with an error. IfmaxIterations
is not set, a server-defined value may be used -
maxDepth
: Maximum path depth for sub-edges expansion. This can be used to limit the depth of the traversal to a sensible amount. This should especially be used for big graphs to limit the traversal to some sensible amount, and for graphs containing cycles to prevent infinite traversals. The maximum depth defaults to 256, with the chance of this value being non-sensical. For several graphs, a much lower maximum depth is sensible, whereas for other, more list-oriented graphs a higher depth should be used -
paths
: Iftrue
, the paths encountered during the traversal will also be returned along with each traversed vertex. Iffalse
, only the encountered vertices will be returned. -
uniqueness
: An optional document with the following attributes:vertices
:none
: No vertex uniqueness is enforcedglobal
: A vertex may be visited at most once. This is the default.path
: A vertex is visited only if not already contained in the current traversal path
edges
:none
: No edge uniqueness is enforcedglobal
: An edge may be visited at most once. This is the defaultpath
: An edge is visited only if not already contained in the current traversal path
-
followEdges
: An optional list of example edge documents that the traversal will expand into. If no examples are given, the traversal will follow all edges. If one or many edge examples are given, the traversal will only follow an edge if it matches at least one of the specified examples.followEdges
can also be a string with the name of an AQL user-defined function that should be responsible for checking if an edge should be followed. In this case, the AQL function will is expected to have the following signature:function (config, vertex, edge, path)
The function is expected to return a boolean value. If it returns
true
, the edge will be followed. Iffalse
is returned, the edge will be ignored. -
filterVertices
: An optional list of example vertex documents that the traversal will treat specially. If no examples are given, the traversal will handle all encountered vertices equally. If one or many vertex examples are given, the traversal will exclude any non-matching vertex from the result and/or not descend into it. Optionally,filterVertices
can contain the name of a user-defined AQL function that should be responsible for filtering. If so, the AQL function is expected to have the following signature:function (config, vertex, path)
If a custom AQL function is used, it is expected to return one of the following values:
[ ]
: Include the vertex in the result and descend into its connected edges[ "prune" ]
: Will include the vertex in the result but not descend into its connected edges[ "exclude" ]
: Will not include the vertex in the result but descend into its connected edges[ "prune", "exclude" ]
: Will completely ignore the vertex and its connected edges
-
vertexFilterMethod
: Only useful in conjunction withfilterVertices
and if no user-defined AQL function is used. If specified, it will influence how vertices are handled that don't match the examples infilterVertices
:[ "prune" ]
: Will include non-matching vertices in the result but not descend into them[ "exclude" ]
: Will not include non-matching vertices in the result but descend into them[ "prune", "exclude" ]
: Will neither include non-matching vertices in the result nor descend into them
The result of the TRAVERSAL function is a list of traversed points. Each point is a document consisting of the following attributes:
vertex
: The vertex at the traversal pointpath
: The path history for the traversal point. The path is a document with the attributesvertices
andedges
, which are both lists. Note thatpath
is only present in the result if thepaths
attribute is set in the @FA{options}
Example calls:
TRAVERSAL(friends, friendrelations, "friends/john", "outbound", { strategy: "depthfirst", order: "postorder", itemOrder: "backward", maxDepth: 6, paths: true }) // filtering on specific edges (by specifying example edges) TRAVERSAL(friends, friendrelations, "friends/john", "outbound", { strategy: "breadthfirst", order: "preorder", itemOrder: "forward", followEdges: [ { type: "knows" }, { state: "FL" } ] }) // filtering on specific edges and vertices TRAVERSAL(friends, friendrelations, "friends/john", "outbound", { strategy: "breadthfirst", order: "preorder", itemOrder: "forward", followEdges: [ { type: "knows" }, { state: "FL" } ], filterVertices: [ { isActive: true }, { isDeleted: false } ], vertexFilterMethod: [ "prune", "exclude" ] }) // using user-defined AQL functions for edge and vertex filtering TRAVERSAL(friends, friendrelations, "friends/john", "outbound", { followEdges: "myfunctions::checkedge", filterVertices: "myfunctions::checkvertex" }) // to register the custom AQL functions, execute something in the fashion of the // following commands in arangosh once: var aqlfunctions = require("org/arangodb/aql/functions"); // these are the actual filter functions aqlfunctions.register("myfunctions::checkedge", function (config, vertex, edge, path) { return (edge.type !== 'dislikes'); // don't follow these edges }, false); aqlfunctions.register("myfunctions::checkvertex", function (config, vertex, path) { if (vertex.isDeleted || ! vertex.isActive) { return [ "prune", "exclude" ]; // exclude these and don't follow them } return [ ]; // include everything else }, false);
-
@FN{TRAVERSAL_TREE(@FA{vertexcollection}, @FA{edgecollection}, @FA{startVertex}, @FA{direction}, @FA{connectName}, @FA{options})}: Traverses the graph described by @FA{vertexcollection} and @FA{edgecollection}, starting at the vertex identified by id @FA{startVertex} and creates a hierchical result. Vertex connectivity is establish by inserted an attribute which has the name specified via the @FA{connectName} parameter. Connected vertices will be placed in this attribute as a list.
The @FA{options} are the same as for the
TRAVERSAL
function, except that the result will be set up in a way that resembles a depth-first, pre-order visitation result. Thus, thestrategy
andorder
attributes of the @FA{options} attribute will be ignored.Example calls:
TRAVERSAL_TREE(friends, friendrelations, "friends/john", "outbound", "likes", { itemOrder: "forward" })
When using one of AQL's graph functions please make sure that the graph does not contain cycles, or that you at least specify some maximum depth or uniqueness criteria for a traversal.
If no bounds are set, a traversal might run into an endless loop in a cyclic graph or sub-graph, and even in a non-cyclic graph, traversing far into the graph might consume a lot of processing time and memory for the result set.
-
@FN{SHORTEST_PATH(@FA{vertexcollection}, @FA{edgecollection}, @FA{startVertex}, @FA{endVertex}, @FA{direction}, @FA{options})}: Determines the first shortest path from the @FA{startVertex} to the @FA{endVertex}. Both vertices must be present in the vertex collection specified in @FA{vertexcollection}, and any connecting edges must be present in the collection specified by @FA{edgecollection}. Vertex connectivity is specified by the @FA{direction} parameter:
"outbound"
: Vertices are connected in_from
to_to
order"inbound"
: Vertices are connected in_to
to_from
order"any"
: Vertices are connected in both_to
to_from
and in_from
to_to
order The search is aborted when a shortest path is found. Only the first shortest path will be returned. Any vertex will be visited at most once by the search.
Additional options for the traversal can be provided via the @FA{options} document:
-
maxIterations
: Maximum number of iterations in the search. This number can be set to bound long-running searches. When a search performs as many iterations as themaxIterations
value, the search will abort with an error. IfmaxIterations
is not set, a server-defined value may be used. -
paths
: Iftrue
, the result will not only contain the vertices along the shortest path, but also the connecting edges. Iffalse
, only the encountered vertices will be returned. -
distance
: An optional custom function to be used when calculating the distance between a vertex and a neighboring vertex. The expected function signature is:function (config, vertex1, vertex2, edge)
Both vertices and the connecting edge will be passed into the function. The function is expected to return a numeric value that expresses the distance between the two vertices. Higher values will mean higher distances, giving the connection a lower priority in further analysis. If no custom distance function is specified, all vertices are assumed to have the same distance (1) to each other. If a function name is specified, it must have been registered as a regular user-defined AQL function.
-
followEdges
: An optional list of example edge documents that the search will expand into. If no examples are given, the search will follow all edges. If one or many edge examples are given, the search will only follow an edge if it matches at least one of the specified examples.followEdges
can also be a string with the name of an AQL user-defined function that should be responsible for checking if an edge should be followed. In this case, the AQL function will is expected to have the following signature:function (config, vertex, edge, path)
The function is expected to return a boolean value. If it returns
true
, the edge will be followed. Iffalse
is returned, the edge will be ignored. -
filterVertices
: An optional list of example vertex documents that the search will treat specially. If no examples are given, the search will handle all encountered vertices equally. If one or many vertex examples are given, the search will exclude the vertex from the result and/or not descend into it. Optionally,filterVertices
can contain the name of a user-defined AQL function that should be responsible for filtering. If so, the AQL function is expected to have the following signature:function (config, vertex, path)
If a custom AQL function is used, it is expected to return one of the following values:
[ ]
: Include the vertex in the result and descend into its connected edges[ "prune" ]
: Will include the vertex in the result but not descend into its connected edges[ "exclude" ]
: Will not include the vertex in the result but descend into its connected edges[ "prune", "exclude" ]
: Will completely ignore the vertex and its connected edges
The result of the SHORTEST_PATH function is a list with the components of the shortest path. Each component is a document consisting of the following attributes:
vertex
: The vertex at the traversal pointpath
: The path history for the traversal point. The path is a document with the attributesvertices
andedges
, which are both lists. Note thatpath
is only present in the result if thepaths
attribute is set in the @FA{options}.
Example calls:
SHORTEST_PATH(cities, motorways, "cities/CGN", "cities/MUC", "outbound", { paths: true }) // using a user-defined distance function SHORTEST_PATH(cities, motorways, "cities/CGN", "cities/MUC", "outbound", { paths: true, distance: "myfunctions::citydistance" }) // using a user-defined function to filter edges SHORTEST_PATH(cities, motorways, "cities/CGN", "cities/MUC", "outbound", { paths: true, followEdges: "myfunctions::checkedge" }) // to register a custom AQL distance function, execute something in the fashion of the // following commands in arangosh once: var aqlfunctions = require("org/arangodb/aql/functions"); // this is the actual distance function aqlfunctions.register("myfunctions::distance", function (config, vertex1, vertex2, edge) { return Math.sqrt(Math.pow(vertex1.x - vertex2.x) + Math.pow(vertex1.y - vertex2.y)); }, false); // this is the filter function for the edges aqlfunctions.register("myfunctions::checkedge", function (config, vertex, edge, path) { return (edge.underConstruction === false); // don't follow these edges }, false);
-
@FN{EDGES(@FA{edgecollection}, @FA{startvertex}, @FA{direction}, @FA{edgeexamples})}: Return all edges connected to the vertex @FA{startvertex} as a list. The possible values for @FA{direction} are:
outbound
: Return all outbound edgesinbound
: Return all inbound edgesany
: Return outbound and inbound edges
The @FA{edgeexamples} parameter can optionally be used to restrict the results to specific edge connections only. The matching is then done via the @LIT{MATCHES} function. To not restrict the result to specific connections, @FA{edgeexamples} should be left unspecified.
Example calls:
EDGES(friendrelations, "friends/john", "outbound") EDGES(friendrelations, "friends/john", "any", [ { "$label": "knows" } ])
-
@FN{NEIGHBORS(@FA{vertexcollection}, @FA{edgecollection}, @FA{startvertex}, @FA{direction}, @FA{edgeexamples})}: Return all neighbors that are directly connected to the vertex @FA{startvertex} as a list. The possible values for @FA{direction} are:
outbound
: Return all outbound edgesinbound
: Return all inbound edgesany
: Return outbound and inbound edges
The @FA{edgeexamples} parameter can optionally be used to restrict the results to specific edge connections only. The matching is then done via the @LIT{MATCHES} function. To not restrict the result to specific connections, @FA{edgeexamples} should be left unspecified.
Example calls:
NEIGHBORS(friends, friendrelations, "friends/john", "outbound") NEIGHBORS(users, usersrelations, "users/john", "any", [ { "$label": "recommends" } ] )
@subsubsection AqlFunctionsControl Control flow functions
AQL offers the following functions to let the user control the flow of operations:
-
@FN{NOT_NULL(@FA{alternative}, ...)}: Returns the first alternative that is not
null
, andnull
if all alternatives arenull
themselves -
@FN{FIRST_LIST(@FA{alternative}, ...)}: Returns the first alternative that is a list, and
null
if none of the alternatives is a list -
@FN{FIRST_DOCUMENT(@FA{alternative}, ...)}: Returns the first alternative that is a document, and
null
if none of the alternatives is a document
@subsubsection AqlFunctionsMisc Miscellaneous functions
Finally, AQL supports the following functions that do not belong to any of the other function categories:
-
@FN{COLLECTIONS()}: Returns a list of collections. Each collection is returned as a document with attributes
name
and_id
-
@FN{CURRENT_DATABASE()}: Returns the name of the current database.
-
@FN{CURRENT_USER()}: Returns the name of the current user. The current user is the user account name that was specified in the
Authorization
HTTP header of the request. It will only be populated if authentication on the server is turned on, and if the query was executed inside a request context. Otherwise, the return value of this function will benull
. -
@FN{DOCUMENT(@FA{collection}, @FA{id})}: Returns the document which is uniquely identified by the @FA{id}. ArangoDB will try to find the document using the
_id
value of the document in the specified collection. If there is a mismatch between the @FA{collection} passed and the collection specified in @FA{id}, thennull
will be returned. Additionally, if the @FA{collection} matches the collection value specified in @FA{id} but the document cannot be found,null
will be returned. This function also allows @FA{id} to be a list of ids. In this case, the function will return a list of all documents that could be found.Examples:
DOCUMENT(users, "users/john") DOCUMENT(users, "john") DOCUMENT(users, [ "users/john", "users/amy" ]) DOCUMENT(users, [ "john", "amy" ])
Note: The @FN{DOCUMENT} function is polymorphic since ArangoDB 1.4. It can now be used with a single parameter @FA{id} as follows:
-
@FN{DOCUMENT(@FA{id})}: In this case, @FA{id} must either be a document handle string (consisting of collection name and document key) or a list of document handle strings, e.g.
DOCUMENT("users/john") DOCUMENT([ "users/john", "users/amy" ])
-
@FN{SKIPLIST(@FA{collection}, @FA{condition}, @FA{skip}, @FA{limit})}: Return all documents from a skiplist index on collection @FA{collection} that match the specified @FA{condition}. This is a shortcut method to use a skiplist index for retrieving specific documents in indexed order. The skiplist index supports equality and less than/greater than queries. The @FA{skip} and @FA{limit} parameters are optional but can be specified to further limit the results:
SKIPLIST(test, { created: [[ '>', 0 ]] }, 0, 100) SKIPLIST(test, { age: [[ '>', 25 ], [ '<=', 65 ]] }) SKIPLIST(test, { a: [[ '==', 10 ]], b: [[ '==', 25 ]] }
The @FA{condition} document must contain an entry for each attribute that is contained in the index. It is not allowed to specify just a subset of attributes that are present in an index. Additionally the attributes in the @FA{condition} document must be specified in the same order as in the index. If no suitable skiplist index is found, an error will be raised and the query will be aborted.
High-level operations
FOR
The FOR
keyword can be to iterate over all elements of a list.
The general syntax is:
FOR variable-name IN expression
Each list element returned by expression
is visited exactly once. It is
required that expression
returns a list in all cases. The empty list is
allowed, too. The current list element is made available for further processing
in the variable specified by variable-name
.
FOR u IN users
RETURN u
This will iterate over all elements from the list users
(note: this list
consists of all documents from the collection named "users" in this case) and
make the current list element available in variable u
. u
is not modified in
this example but simply pushed into the result using the RETURN
keyword.
Note: When iterating over collection-based lists as shown here, the order of
documents is undefined unless an explicit sort order is defined using a SORT
statement.
The variable introduced by FOR
is available until the scope the FOR
is
placed in is closed.
Another example that uses a statically declared list of values to iterate over:
FOR year IN [ 2011, 2012, 2013 ]
RETURN { "year" : year, "isLeapYear" : year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) }
Nesting of multiple FOR
statements is allowed, too. When FOR
statements are
nested, a cross product of the list elements returned by the individual FOR
statements will be created.
FOR u IN users
FOR l IN locations
RETURN { "user" : u, "location" : l }
In this example, there are two list iterations: an outer iteration over the list
users
plus an inner iteration over the list locations
. The inner list is
traversed as many times as there are elements in the outer list. For each
iteration, the current values of users
and locations
are made available for
further processing in the variable u
and l
.
RETURN
The RETURN
statement can (and must) be used to produce the result of a query.
It is mandatory to specify a RETURN
statement at the end of each block in a
query, otherwise the query result would be undefined.
The general syntax for return
is:
RETURN expression
The expression
returned by RETURN
is produced for each iteration the
RETURN
statement is placed in. That means the result of a RETURN
statement
is always a list (this includes the empty list). To return all elements from
the currently iterated list without modification, the following simple form can
be used:
FOR variable-name IN expression
RETURN variable-name
As RETURN
allows specifying an expression, arbitrary computations can be
performed to calculate the result elements. Any of the variables valid in the
scope the RETURN
is placed in can be used for the computations.
Note: Return will close the current scope and eliminate all local variables in it.
FILTER
The FILTER
statement can be used to restrict the results to elements that
match an arbitrary logical condition. The general syntax is:
FILTER condition
condition
must be a condition that evaluates to either false
or true
. If
the condition result is false, the current element is skipped, so it will not be
processed further and not be part of the result. If the condition is true, the
current element is not skipped and can be further processed.
FOR u IN users
FILTER u.active == true && u.age < 39
RETURN u
In the above example, all list elements from users
will be included that have
an attribute active
with value true
and that have an attribute age
with a
value less than 39
. All other elements from users
will be skipped and not be
included the result produced by RETURN
.
It is allowed to specify multiple FILTER
statements in a query, and even in
the same block. If multiple FILTER
statements are used, their results will be
combined with a logical and, meaning all filter conditions must be true to
include an element.
FOR u IN users
FILTER u.active == true
FILTER u.age < 39
RETURN u
SORT
The SORT
statement will force a sort of the list of already produced
intermediate results in the current block. SORT
allows specifying one or
multiple sort criteria and directions. The general syntax is:
SORT expression direction
Specifying the direction
is optional. The default (implicit) direction for a
sort is the ascending order. To explicitly specify the sort direction, the
keywords ASC
(ascending) and DESC
can be used. Multiple sort criteria can be
separated using commas.
Note: when iterating over collection-based lists, the order of documents is
always undefined unless an explicit sort order is defined using SORT
.
FOR u IN users
SORT u.lastName, u.firstName, u.id DESC
RETURN u
LIMIT
The LIMIT
statement allows slicing the list of result documents using an
offset and a count. It reduces the number of elements in the result to at most
the specified number. Two general forms of LIMIT
are followed:
LIMIT count
LIMIT offset, count
The first form allows specifying only the count
value whereas the second form
allows specifying both offset
and count
. The first form is identical using
the second form with an offset
value of 0
.
The offset
value specifies how many elements from the result shall be
discarded. It must be 0 or greater. The count
value specifies how many
elements should be at most included in the result.
FOR u IN users
SORT u.firstName, u.lastName, u.id DESC
LIMIT 0, 5
RETURN u
LET
The LET
statement can be used to assign an arbitrary value to a variable. The
variable is then introduced in the scope the LET
statement is placed in. The
general syntax is:
LET variable-name = expression
LET
statements are mostly used to declare complex computations and to avoid
repeated computations of the same value at multiple parts of a query.
FOR u IN users
LET numRecommendations = LENGTH(u.recommendations)
RETURN { "user" : u, "numRecommendations" : numRecommendations, "isPowerUser" : numRecommendations >= 10 }
In the above example, the computation of the number of recommendations is
factored out using a LET
statement, thus avoiding computing the value twice in
the RETURN
statement.
Another use case for LET
is to declare a complex computation in a subquery,
making the whole query more readable.
FOR u IN users
LET friends = (
FOR f IN friends
FILTER u.id == f.userId
RETURN f
)
LET memberships = (
FOR m IN memberships
FILTER u.id == m.userId
RETURN m
)
RETURN { "user" : u, "friends" : friends, "numFriends" : LENGTH(friends), "memberShips" : memberships }
COLLECT
The COLLECT
keyword can be used to group a list by one or multiple group
criteria. The two general syntaxes for COLLECT
are:
COLLECT variable-name = expression
COLLECT variable-name = expression INTO groups
The first form only groups the result by the defined group criteria defined by
expression
. In order to further process the results produced by COLLECT
, a
new variable (specified by variable-name
) is introduced. This variable
contains the group value.
The second form does the same as the first form, but additionally introduces a
variable (specified by groups
) that contains all elements that fell into the
group. Specifying the INTO
clause is optional-
FOR u IN users
COLLECT city = u.city INTO g
RETURN { "city" : city, "users" : g }
In the above example, the list of users
will be grouped by the attribute
city
. The result is a new list of documents, with one element per distinct
city
value. The elements from the original list (here: users
) per city are
made available in the variable g
. This is due to the INTO
clause.
COLLECT
also allows specifying multiple group criteria. Individual group
criteria can be separated by commas.
FOR u IN users
COLLECT first = u.firstName, age = u.age INTO g
RETURN { "first" : first, "age" : age, "numUsers" : LENGTH(g) }
In the above example, the list of users
is grouped by first names and ages
first, and for each distinct combination of first name and age, the number of
users found is returned.
Note: The COLLECT
statement eliminates all local variables in the current
scope. After COLLECT
only the variables introduced by COLLECT
itself are
available.
Advanced features
Subqueries
Wherever an expression is allowed in AQL, a subquery can be placed. A subquery is a query part that can introduce its own local variables without affecting variables and values in its outer scope(s).
It is required that subqueries be put inside parentheses (
and )
to
explicitly mark their start and end points:
FOR u IN users
LET recommendations = (
FOR r IN recommendations
FILTER u.id == r.userId
SORT u.rank DESC
LIMIT 10
RETURN r
)
RETURN { "user" : u, "recommendations" : recommendations }
FOR u IN users
COLLECT city = u.city INTO g
RETURN { "city" : city, "numUsers" : LENGTH(g), "maxRating": MAX(
FOR r IN g
RETURN r.user.rating
) }
Subqueries might also include other subqueries themselves.
Variable expansion
In order to access a named attribute from all elements in a list easily, AQL
offers the shortcut operator [*]
for variable expansion.
Using the [*]
operator with a variable will iterate over all elements in the
variable thus allowing to access a particular attribute of each element. It is
required that the expanded variable is a list. The result of the [*]
operator is again a list.
FOR u IN users
RETURN { "user" : u, "friendNames" : u.friends[*].name }
In the above example, the attribute name
is accessed for each element in the
list u.friends
. The result is a flat list of friend names, made available as
the attribute friendNames
.
@BNAVIGATE_Aql