operations
Table of Contents
Functions
- aqlAsc() : string
- Builds an ascending AQL `SORT` expression for the given attribute key.
- aqlCollect() : string
- Builds an AQL `COLLECT` clause for grouping, aggregation, and counting.
- aqlCollectReturn() : string
- Builds the `RETURN` clause that follows an AQL `COLLECT` produced by {@see aqlCollect()}.
- aqlDesc() : string
- Builds an descending AQL `SORT` expression for the given attribute key.
- aqlFilter() : string|null
- Builds an AQL `FILTER` clause from one or more logical conditions.
- aqlFor() : string
- Builds an ArangoDB AQL `FOR` clause, optionally including `SEARCH` and `OPTIONS` segments.
- aqlInsert() : string
- Builds an AQL `INSERT` statement.
- aqlLet() : string
- The LET operation defines a variable within an AQL query, which can then be used in subsequent expressions.
- aqlLimit() : string
- Provides helpers to build AQL `LIMIT` clauses for ArangoDB queries, supporting **offsets**, **parameter binding**, and **dynamic query generation**.
- aqlOptions() : string
- Builds the AQL `OPTIONS` clause from the provided options.
- aqlPrune() : string|null
- Builds an AQL `PRUNE` clause from one or more logical conditions.
- aqlRemove() : string
- Remove one or multiple documents from a collection using an AQL `REMOVE` operation.
- aqlReplace() : string
- The REPLACE statement replaces an existing document with a new one, removing any attributes that are not explicitly set in the provided `doc` while preserving immutable system attributes (`_id`, `_key`, `_rev`).
- aqlRepsert() : string
- Prepare a REPSERT query to replace an existing document or insert a new one if it does not exist.
- aqlReturn() : string
- Builds an AQL `RETURN` clause from a given expression.
- aqlScoredSearch() : string
- Builds a complete, relevance-ranked AQL search query over an ArangoSearch View.
- aqlSearch() : string
- Builds an AQL `SEARCH` clause for a query, with optional Analyzer wrapping and an optional `OPTIONS` object.
- aqlSort() : string
- Builds an AQL `SORT` clause from a string or an array of sort expressions.
- aqlTraversal() : string
- Builds a full **AQL traversal clause** for ArangoDB queries.
- aqlTraversalRange() : string
- Builds an AQL traversal range clause (e.g., `1..1`, `1..5`, `..2`, `3..`).
- aqlUpdate() : string
- Partially modifies a document with the given attributes, by adding new and updating existing attributes.
- aqlUpsert() : string
- Prepare the query to update an existing document, or creates a new document if it does not exist.
- aqlVectorSearch() : string
- Builds a complete AQL approximate nearest-neighbour (ANN) query over a vector index.
- aqlWindow() : string
- Builds an AQL `WINDOW` clause for sliding-window aggregation (running totals, rolling averages, and other statistical properties over related rows).
- aqlWindowBounds() : string
- Serializes the `{ preceding: …, following: … }` bounds object of a `WINDOW` clause.
- aqlWith() : string
- Generates an AQL `WITH` clause for one or more collections.
Functions
aqlAsc()
Builds an ascending AQL `SORT` expression for the given attribute key.
aqlAsc(string $key[, string|null $prefix = null ]) : string
This helper simplifies the creation of SORT clauses by combining the
provided key (and optional prefix) with the ASC order keyword.
The resulting string can be directly injected into an AQL statement or
composed with other expressions (e.g., using aqlSort()).
Example: basic usage
echo aqlAsc('age');
// → "age ASC"
Example: with prefix
echo aqlAsc('name', 'u');
// → "u.name ASC"
Example: combined in a SORT clause
echo 'SORT ' . aqlAsc('score', 'player');
// → "SORT player.score ASC"
Parameters
- $key : string
-
The attribute name to sort by (e.g.,
'age','createdAt','score'). - $prefix : string|null = null
-
Optional variable or collection prefix (e.g.,
'u'for"u.age").
Tags
Return values
string —The formatted ascending sort expression, e.g. "doc.name ASC".
aqlCollect()
Builds an AQL `COLLECT` clause for grouping, aggregation, and counting.
aqlCollect([array<string|int, mixed> $init = [] ]) : string
Supported $init keys
| Key | Type | Description |
|---|---|---|
AQL::ASSIGN |
array | Grouping variables, e.g., ['group' => 'doc.type']. |
AQL::AGGREGATE |
array | Aggregation expressions, e.g., ['total' => 'SUM(doc.value)']. |
AQL::INTO |
string | Variable name to collect documents into (e.g., 'groupDocs'). |
AQL::PROJECTION |
string | Projection expression for the INTO clause (e.g., 'doc.name'). |
AQL::KEEP |
array | An array of variable names to keep (e.g., ['var1', 'var2']). |
AQL::WITH_COUNT |
string | Variable name for the count (e.g., AQL::LENGTH). |
AQL::OPTIONS |
array | COLLECT options (e.g., ['method' => 'sorted']). |
Note:
AQL::AGGREGATEandAQL::WITH_COUNTare mutually exclusive in AQL. When both are supplied,AGGREGATEtakes precedence andWITH COUNT INTOis dropped. To count alongside other aggregates, express the count as an aggregate (e.g.,['n' => 'LENGTH(1)']).
Examples
Simple Count (for countVertices):
echo aqlCollect([ AQL::WITH_COUNT => AQL::LENGTH ]);
// COLLECT WITH COUNT INTO length
Grouping and Aggregating:
echo aqlCollect
([
AQL::ASSIGN => ['type' => 'doc.type'],
AQL::AGGREGATE => ['count' => 'LENGTH(1)']
]);
// COLLECT type = doc.type AGGREGATE count = LENGTH(1)
Grouping with INTO:
echo aqlCollect
([
AQL::ASSIGN => ['type' => 'doc.type'],
AQL::INTO => 'items',
AQL::PROJECTION => '{ name: doc.name, age: doc.age }'
]);
// COLLECT type = doc.type INTO items = { name: doc.name, age: doc.age }
Parameters
- $init : array<string|int, mixed> = []
-
Associative array of collect options.
Tags
Return values
string —The compiled AQL COLLECT clause, or an empty string if invalid.
aqlCollectReturn()
Builds the `RETURN` clause that follows an AQL `COLLECT` produced by {@see aqlCollect()}.
aqlCollectReturn([array<string|int, mixed> $spec = [] ][, string|null $explicit = null ]) : string
After a COLLECT, the iteration variable (e.g. doc) is out of scope: only the
grouping variables, the aggregate variables and the optional WITH COUNT variable
remain usable. This helper derives a valid projection from the very same $spec
given to aqlCollect(), so the two always stay in sync.
Behaviour
- An explicit, non-empty
$explicitexpression always wins (RETURN <expr>). - Otherwise the projection is derived from the spec:
- grouping keys (
array_keys(AQL::ASSIGN)) + aggregate keys (array_keys(AQL::AGGREGATE)), - plus the
AQL::WITH_COUNTvariable when present.
- grouping keys (
AQL::AGGREGATEandAQL::WITH_COUNTare mutually exclusive (mirrors aqlCollect()): when an aggregate is present the count variable is ignored.- A pure count (no grouping, no aggregate, only
WITH_COUNT) returns the scalar count (RETURN length), not an object. - When nothing can be projected, an empty string is returned.
AQL::INTO collected documents are intentionally NOT auto-projected (they may be huge);
pass an $explicit projection to expose them.
Examples
echo aqlCollectReturn([ AQL::ASSIGN => ['status' => 'doc.status'] ]);
// RETURN { status }
echo aqlCollectReturn([ AQL::ASSIGN => ['category' => 'doc.category'], AQL::WITH_COUNT => 'count' ]);
// RETURN { category, count }
echo aqlCollectReturn([ AQL::WITH_COUNT => 'length' ]);
// RETURN length
echo aqlCollectReturn([ AQL::ASSIGN => ['y' => 'DATE_YEAR(doc.created)'] ], '{ year: y }');
// RETURN { year: y }
Parameters
- $spec : array<string|int, mixed> = []
-
The same associative spec passed to aqlCollect().
- $explicit : string|null = null
-
An explicit RETURN expression overriding the derivation.
Tags
Return values
string —The compiled AQL RETURN clause, or an empty string when nothing to project.
aqlDesc()
Builds an descending AQL `SORT` expression for the given attribute key.
aqlDesc(string $key[, string|null $prefix = null ]) : string
This helper simplifies the creation of SORT clauses by combining the
provided key (and optional prefix) with the DESC order keyword.
The resulting string can be directly injected into an AQL statement or
composed with other expressions (e.g., using aqlSort()).
Example: basic usage
echo aqlDesc('age');
// → "age DESC"
Example: with prefix
echo aqlDesc('name', 'u');
// → "u.name DESC"
Example: combined in a SORT clause
echo 'SORT ' . aqlDesc('score', 'player');
// → "SORT player.score DESC"
Parameters
- $key : string
-
The attribute name to sort by (e.g.,
'age','createdAt','score'). - $prefix : string|null = null
-
Optional variable or collection prefix (e.g.,
'u'for"u.age").
Tags
Return values
string —The formatted descending sort expression, e.g. "doc.name DESC".
aqlFilter()
Builds an AQL `FILTER` clause from one or more logical conditions.
aqlFilter([string|array<string|int, mixed>|null $conditions = null ][, string $logicalOperator = Logic::AND ][, bool $useParentheses = false ]) : string|null
The FILTER operation restricts the results to elements that match arbitrary logical conditions.
Syntax:
FILTER expression
The expression must evaluate to either true or false.
Example:
use function oihana\arango\db\operations\aqlFilter;
echo aqlFilter( 'user.age > 18' ) . PHP_EOL;
// FILTER user.age > 18
echo aqlFilter( [ 'user.active == true', 'user.age >= 18' ] ) . PHP_EOL;
// FILTER user.active == true && user.age >= 18
echo aqlFilter( [ 'x > 5', 'y < 10' ], '||' ) . PHP_EOL;
// FILTER x > 5 || y < 10
echo aqlFilter(); // null
Parameters
- $conditions : string|array<string|int, mixed>|null = null
-
The expression(s) to evaluate in the FILTER operation.
- $logicalOperator : string = Logic::AND
-
The logical operator used to join conditions if
$conditionsis an array (default&&). - $useParentheses : bool = false
-
Whether to wrap the result in parentheses.
Tags
Return values
string|null —The compiled AQL FILTER clause, or null if no valid condition was provided.
aqlFor()
Builds an ArangoDB AQL `FOR` clause, optionally including `SEARCH` and `OPTIONS` segments.
aqlFor([array<string|int, mixed> $init = [] ]) : string
The generated query follows this canonical form:
FOR <variableName> IN <expression> [SEARCH <searchExpression>] [OPTIONS { ... }]
This function simplifies the construction of AQL loops by automatically
compiling the parts (IN, SEARCH, OPTIONS) using helper functions such as
aqlSearch() and aqlOptions().
Supported $init keys
| Key | Type | Description |
|---|---|---|
AQL::DOC_REF |
`string | null` |
AQL::IN |
`string | null` |
AQL::SEARCH |
`string | null` |
AQL::OPTIONS |
`array | object |
Example: basic usage
echo aqlFor([
AQL::DOC_REF => 'doc',
AQL::IN => 'users'
]);
// → "FOR doc IN users"
Example: with SEARCH and OPTIONS
echo aqlFor
([
AQL::DOC_REF => 'u',
AQL::IN => 'searchUsers',
AQL::SEARCH => 'u.active == true',
AQL::OPTIONS =>
[
'indexHint' => 'byActive',
'forceIndexHint' => true,
'disableIndex' => false,
'useCache' => true,
'lookahead' => 5
]
]);
// → "FOR u IN searchUsers SEARCH u.active == true OPTIONS {\"indexHint\":\"byActive\",\"forceIndexHint\":true,\"disableIndex\":false,\"useCache\":true,\"lookahead\":5}"
Example: using a ForOptions schema object
use oihana\arango\db\options\ForOptions;
$opts = new ForOptions([
'useCache' => false,
'lookahead' => 3
]);
echo aqlFor
`([
AQL::DOC_REF => 'd',
AQL::IN => 'documents',
AQL::OPTIONS => $opts
]);
// → "FOR d IN documents OPTIONS {\"useCache\":false,\"lookahead\":3}"
Parameters
- $init : array<string|int, mixed> = []
-
An associative array defining the
FORclause elements. Example structure:[ AQL::DOC_REF => 'doc', AQL::IN => 'users', AQL::SEARCH => 'doc.age > 30', AQL::OPTIONS => [ 'useCache' => true ] ]
Tags
Return values
string —The complete AQL FOR clause, or an empty string if no valid input is provided.
aqlInsert()
Builds an AQL `INSERT` statement.
aqlInsert([array<string|int, mixed> $init = [] ][, array<string|int, mixed>|null &$binds = null ][, string|null $queryID = null ]) : string
This method dynamically constructs an ArangoDB AQL query of the form:
INSERT {document} INTO collection OPTIONS {...} RETURN NEW
It supports binding collection names, attaching additional insert options, and optionally returning the newly inserted document.
The $init array may contain:
AQL::DOCUMENT(array|object|string) — The document to insert.AQL::COLLECTION(string) — The target collection name.AQL::BIND_COLLECTION(bool) — Whether to bind the collection as a variable.AQL::QUERY_ID(string) — An optional query identifier to prepend the default name of the bind collection variable.AQL::RAW_VALUES(array) — Keys in the document whose values should be treated as raw AQL expressions.AQL::USE_SPACE(bool) — Whether to add spaces around braces and commas for readability.
Parameters
- $init : array<string|int, mixed> = []
-
An associative array containing the insert parameters.
- $binds : array<string|int, mixed>|null = null
-
A reference array to hold bind variables for the query.
- $queryID : string|null = null
Tags
Return values
string —The compiled AQL INSERT query string, ready to be executed.
aqlLet()
The LET operation defines a variable within an AQL query, which can then be used in subsequent expressions.
aqlLet(string $variableName, string $expression[, bool $useParentheses = false ][, bool $trim = false ]) : string
A variable defined with LET exists only for the scope of the query or subquery.
Syntax:
LET variableName = expression
Example usage:
$query = let('total', 'SUM(doc.amount)');
// LET total = SUM(doc.amount)
Another examples:
$query = let('userName', "CONCAT(user.firstName, ' ', user.lastName)");
// LET userName = CONCAT(user.firstName, ' ', user.lastName)
$query = let( 'surface', 'doc.width * doc.height' , true );
// LET surface = ( doc.width * doc.height )
Parameters
- $variableName : string
- $expression : string
- $useParentheses : bool = false
- $trim : bool = false
Tags
Return values
stringaqlLimit()
Provides helpers to build AQL `LIMIT` clauses for ArangoDB queries, supporting **offsets**, **parameter binding**, and **dynamic query generation**.
aqlLimit(int $limit[, int $offset = 0 ][, array<string|int, mixed>|null &$binds = null ]) : string
The LIMIT clause in ArangoDB is used to:
- Restrict the number of results returned by a query.
- Optionally skip a number of documents using an offset.
- Use bind parameters instead of hardcoded values to improve performance and prevent query plan cache invalidation. AQL Syntax*
LIMIT <count>
LIMIT <offset>, <count>
Why use bind parameters for LIMIT/OFFSET?*
Using placeholders like @limit and @offset allows ArangoDB to reuse
the same query execution plan instead of recompiling it each time.
This is particularly efficient when paginating through large datasets.
Examples:
// Simple limit
echo aqlLimit(10);
// LIMIT 10
// Limit with offset
echo aqlLimit(10, 5);
// LIMIT 5, 10
// Limit with bound parameters
$binds = [];
echo aqlLimit(10, 5, $binds);
// LIMIT @offset, @limit
// $binds = [ 'limit' => 10 , 'offset' => 5 ]
Parameters
- $limit : int
-
Maximum number of results to return. Must be > 0 to generate a clause.
- $offset : int = 0
-
Number of results to skip before starting to return results (default 0).
- $binds : array<string|int, mixed>|null = null
-
Optional reference to a binds array for parameterized queries.
Tags
Return values
string —AQL LIMIT clause string, or empty string if $limit <= 0.
aqlOptions()
Builds the AQL `OPTIONS` clause from the provided options.
aqlOptions([array<string|int, mixed> $init = [] ][, string|null $schema = null ]) : string
If $schema is provided, the method attempts to hydrate the options array into
an instance of the specified schema class, provided it exists.
Supported input types for $init[AQL::OPTIONS]:
- Associative array → converted to JSON after cleaning.
- Object implementing JsonSerializable → serialized via
jsonSerialize(). - Generic object → cast to array and encoded as JSON if associative.
- Pre-encoded JSON string → used directly.
If the resulting options are valid, the method returns a properly formatted
OPTIONS { ... } clause. Otherwise, it returns an empty string.
Parameters
- $init : array<string|int, mixed> = []
-
Initial options array. If it contains the key
AQL::OPTIONS, its value will be processed. If absent, the method returns an empty string. - $schema : string|null = null
-
Optional fully-qualified class name of a schema to hydrate options into.
Tags
Return values
string —The generated AQL OPTIONS clause, or an empty string if no valid options are provided.
aqlPrune()
Builds an AQL `PRUNE` clause from one or more logical conditions.
aqlPrune([string|array<string|int, mixed>|null $conditions = null ][, string $logicalOperator = Logic::AND ]) : string|null
The PRUNE operation is used in graph traversals to stop traversing along the current path if the condition is met.
Syntax:
PRUNE expression
The expression must evaluate to either true or false.
Example:
use function oihana\arango\db\operations\aqlPrune;
echo aqlPrune( 'v.age > 40' ) . PHP_EOL;
// PRUNE v.age > 40
echo aqlPrune( [ 'e.type == "friend"', 'v.status == "inactive"' ], '||' ) . PHP_EOL;
// PRUNE e.type == "friend" || v.status == "inactive"
echo aqlPrune(); // null
Parameters
- $conditions : string|array<string|int, mixed>|null = null
-
The expression(s) to evaluate in the FILTER operation.
- $logicalOperator : string = Logic::AND
-
The logical operator used to join conditions if
$conditionsis an array (default&&).
Tags
Return values
string|null —The compiled AQL PRUNE clause, or null if no valid condition was provided.
aqlRemove()
Remove one or multiple documents from a collection using an AQL `REMOVE` operation.
aqlRemove([array{collection?: ?string, expression?: ?string, key?: ?string, options?: ?array{exclusive?: bool, ignoreErrors?: bool, ignoreRevs?: bool, refillIndexCaches?: bool, waitForSync?: bool}} $init = [] ]) : string
This helper builds a valid AQL query string for removing documents based on either:
- a custom key expression, or
- a document key (
_key) and an optional document prefix (defaults todoc).
AQL syntax:
REMOVE <keyExpression> IN <collection> [OPTIONS {...}]
Parameters
- $init : array{collection?: ?string, expression?: ?string, key?: ?string, options?: ?array{exclusive?: bool, ignoreErrors?: bool, ignoreRevs?: bool, refillIndexCaches?: bool, waitForSync?: bool}} = []
-
Initial options array.
- 'collection' : The name of the collection in which the document should be updated.
By default, if the argument is null, use
@@collectionbindVars definition. - 'expression' : The key expression that contains the document identification. If the expression is null or an empty string, the 'key' and 'prefix' definitions are used.
- 'key' : The unique identifier of the document to remove. By default
_key-> REMOVE doc._key IN ... - 'options' : Build a RemoveOptions definition to inject at the end of the query.
- 'prefix' : Optional The name of the document reference. By default
doc-> REMOVE doc._key IN ...
- 'collection' : The name of the collection in which the document should be updated.
By default, if the argument is null, use
Tags
Return values
string —The compiled AQL REMOVE statement.
aqlReplace()
The REPLACE statement replaces an existing document with a new one, removing any attributes that are not explicitly set in the provided `doc` while preserving immutable system attributes (`_id`, `_key`, `_rev`).
aqlReplace([ReplaceOptions|null, with?: string|null} $init = [] ]) : string
Basic Syntax:*
REPLACE `document` IN `collection`
REPLACE `keyExpression` WITH `document` IN `collection`
Parameters
- $init : ReplaceOptions|null, with?: string|null} = []
-
Initial options for the REPLACE statement, with the keys:
- 'collection' : The name of the collection in which the document should be replaced.
- 'doc' : An object and contain the attributes and values to replace.
- 'options' : The default 'options' expression definition
- 'rawValues' : array, keys whose values should be treated as raw AQL expressions - used with the 'with' option)
- 'rawKeys' : array, keys which should be kept raw (their values are not wrapped or converted) - used with the 'with' option)
- 'useSpace' : bool, add spaces around braces and after commas
- 'with' : One or multiple collections for WITH clause -> WITH collection1 [, collection2 [, ... collectionN ] ]
Tags
Return values
stringaqlRepsert()
Prepare a REPSERT query to replace an existing document or insert a new one if it does not exist.
aqlRepsert([array<string|int, mixed> $init = [] ]) : string
UPSERT [ searchExpression | FILTER filterExpression ]
INSERT insertExpression
REPLACE replaceExpression
IN collection
Options in $init:
collection: string|null, name of the collection.filter: array|string|null, optional filter expression.search: array|string|null, the search document.insert: array|string|null, the document to insert if no match is found.replace: array|string|null, the document to replace if a match is found.options: array|QueryOptions|string|JsonSerializable|null, optional upsert options.return: optional expression to define the RETURN clause. Default is Clause::NEW. You can also use Clause::WITH_STATUS to return both the document and the type of operation.
Parameters
- $init : array<string|int, mixed> = []
-
Configuration options for the REPSERT query.
Tags
Return values
string —The generated AQL UPSERT query.
aqlReturn()
Builds an AQL `RETURN` clause from a given expression.
aqlReturn(mixed $expression[, bool $distinct = false ]) : string
A RETURN operation is mandatory at the end of each AQL query block, otherwise the query result would be undefined. Using RETURN at the top level in data modification queries is optional.
Example:
use function oihana\arango\db\operations\aqlReturn;
echo aqlReturn( 'user.name' ) . PHP_EOL;
// RETURN user.name
echo aqlReturn( Clause::NEW ) . PHP_EOL;
// RETURN NEW
echo aqlReturn( 'user.email' , true ) . PHP_EOL;
// RETURN DISTINCT user.email
Parameters
- $expression : mixed
-
The expression to evaluate (array or string).
- $distinct : bool = false
-
Whether to add the DISTINCT keyword in the RETURN clause.
Tags
Return values
string —The compiled AQL RETURN clause, or an empty string if expression is empty.
aqlScoredSearch()
Builds a complete, relevance-ranked AQL search query over an ArangoSearch View.
aqlScoredSearch(string $view, string|array<string|int, mixed> $search, int $limit[, string|null $analyzer = null ][, array<string|int, mixed>|object|string|null $options = null ][, string $scorer = SearchScorer::BM25 ][, float|null $k = null ][, float|null $b = null ][, bool|null $normalize = null ][, int $offset = 0 ][, string $docRef = 'doc' ][, string $scoreRef = 'score' ][, string|null $return = null ]) : string
The generated query follows the canonical scored-search form:
FOR <docRef> IN <view>
SEARCH <expression> [OPTIONS { … }]
LET <scoreRef> = BM25(<docRef>) | TFIDF(<docRef>)
SORT <scoreRef> DESC
LIMIT [<offset>,] <limit>
RETURN <return>
Both scorers rank better matches with higher values, so the sort is
always descending — there is no direction to get wrong. The score is bound
to a LET variable ($scoreRef, default score) so a custom $return
expression can expose it, e.g. 'MERGE(doc, { score: score })'.
The SEARCH segment reuses aqlFor() / aqlSearch(): the
optional $analyzer wraps the expression in ANALYZER(expr, "name") and
the optional $options becomes the SEARCH … OPTIONS { … } object
(hydrated into SearchOptions).
The scorer functions require the indexed fields' Analyzers to have the
"frequency"feature enabled (and"norm"for meaningful BM25 length normalization), otherwise the score is0.
Example: phrase search ranked by BM25
use function oihana\arango\db\functions\search\phrase;
use function oihana\arango\db\operations\aqlScoredSearch;
$aql = aqlScoredSearch
(
view : 'placesView' ,
search : phrase( 'doc.name' , 'scierie' ) ,
limit : 20 ,
analyzer : 'text_fr' ,
) ;
// FOR doc IN placesView SEARCH ANALYZER(PHRASE(doc.name,"scierie"),"text_fr")
// LET score = BM25(doc) SORT score DESC LIMIT 20 RETURN doc
Example: TF-IDF, pagination, and the score in the output
$aql = aqlScoredSearch
(
view : 'articlesView' ,
search : 'doc.text IN TOKENS(@q, "text_en")' ,
limit : 10 ,
offset : 20 ,
scorer : SearchScorer::TFIDF ,
normalize : true ,
return : 'MERGE(doc, { score: score })' ,
) ;
// FOR doc IN articlesView SEARCH doc.text IN TOKENS(@q, "text_en")
// LET score = TFIDF(doc,true) SORT score DESC LIMIT 20, 10 RETURN MERGE(doc, { score: score })
Parameters
- $view : string
-
The ArangoSearch View to query.
- $search : string|array<string|int, mixed>
-
The
SEARCHexpression (kept raw; arrays are compiled likeAQL::SEARCH). - $limit : int
-
The maximum number of matches to return (the
LIMIT). - $analyzer : string|null = null
-
Optional Analyzer name wrapping the expression in
ANALYZER(expr, "name"). - $options : array<string|int, mixed>|object|string|null = null
-
Optional
SEARCH … OPTIONS { … }object (see aqlSearch()). - $scorer : string = SearchScorer::BM25
-
The scoring algorithm:
SearchScorer::BM25(default) orSearchScorer::TFIDF. - $k : float|null = null
-
Optional BM25 term-frequency calibration (BM25 only).
- $b : float|null = null
-
Optional BM25 text-length scaling (BM25 only).
- $normalize : bool|null = null
-
Optional TF-IDF score normalization (TFIDF only).
- $offset : int = 0
-
Optional number of matches to skip (pagination).
- $docRef : string = 'doc'
-
The iteration variable name (default
'doc'). - $scoreRef : string = 'score'
-
The
LETscore variable name (default'score'). - $return : string|null = null
-
Optional
RETURNexpression. Defaults to the iteration variable.
Tags
Return values
string —The complete AQL scored-search query.
aqlSearch()
Builds an AQL `SEARCH` clause for a query, with optional Analyzer wrapping and an optional `OPTIONS` object.
aqlSearch([array<string|int, mixed> $init = [] ]) : string
The SEARCH operation guarantees the use of View indexes for an efficient execution plan. Using FILTER on Views does not utilize indexes and filtering is done as a post-processing step.
$init keys:
AQL::SEARCH— the search expression. Without it everything else is ignored and an empty string is returned.AQL::ANALYZER(optional) — an Analyzer name; the expression is wrapped inANALYZER(expr, "name")via analyzer(), setting the Analyzer for the expression and its nested functions.AQL::SEARCH_OPTIONS(optional) — theSEARCH … OPTIONS { … }object (collections,conditionOptimization,countApproximate,parallelism), accepted as an associative array (hydrated into SearchOptions, unknown keys dropped, null properties omitted), aSearchOptionsinstance, anyJsonSerializable/plain object, or a pre-encoded JSON string — the same tolerance as aqlOptions().
Not to be confused with AQL::OPTIONS, the FOR-level options
(indexHint, useCache, … — see ForOptions):
a FOR over a collection takes AQL::OPTIONS, a SEARCH against a View
takes AQL::SEARCH_OPTIONS. aqlFor() forwards its whole $init
here, so all three keys work through it directly.
Example:
use oihana\arango\db\enums\AQL;
use oihana\arango\db\enums\ConditionOptimization;
use function oihana\arango\db\operations\aqlSearch;
echo aqlSearch([ AQL::SEARCH => 'PHRASE(doc.text, "search phrase", "text_en")' ]) . PHP_EOL;
// SEARCH PHRASE(doc.text, "search phrase", "text_en")
echo aqlSearch
([
AQL::SEARCH => 'PHRASE(doc.text, "search phrase")' ,
AQL::ANALYZER => 'text_en' ,
AQL::SEARCH_OPTIONS => [ 'conditionOptimization' => ConditionOptimization::NONE ] ,
]) . PHP_EOL;
// SEARCH ANALYZER(PHRASE(doc.text, "search phrase"),"text_en") OPTIONS {"conditionOptimization":"none"}
echo aqlSearch(); // ''
Parameters
- $init : array<string|int, mixed> = []
-
Array containing the key
AQL::SEARCHwith the expression to search, and optionallyAQL::ANALYZERandAQL::SEARCH_OPTIONS.
Tags
Return values
string —The compiled AQL SEARCH clause, or an empty string if no search expression is provided.
aqlSort()
Builds an AQL `SORT` clause from a string or an array of sort expressions.
aqlSort(string|array<string|int, mixed>|null $expression) : string
This helper assembles a valid SORT operation for AQL queries.
It accepts a single sort expression or multiple ones (as an array),
and automatically joins them with commas when needed.
Each expression can be generated manually or using helpers like aqlAsc() and aqlDesc().
Example: with a single key
echo aqlSort('user.age ASC');
// → "SORT user.age ASC"
Example: with multiple expressions
echo aqlSort([
aqlAsc('score', 'player'),
aqlDesc('createdAt', 'doc')
]);
// → "SORT player.score ASC, doc.createdAt DESC"
Example: empty or null input
echo aqlSort(null); // → ""
echo aqlSort([]); // → ""
echo aqlSort(''); // → ""
Parameters
- $expression : string|array<string|int, mixed>|null
-
The sort expression(s). Can be:
- a string (
"age ASC") - an array of expressions (
["a ASC", "b DESC"]) nullor empty string for no output
- a string (
Tags
Return values
string —The formatted SORT clause, or an empty string if no expression is provided.
aqlTraversal()
Builds a full **AQL traversal clause** for ArangoDB queries.
aqlTraversal([array{vertexRef?: string, edgeRef?: ?string, pathRef?: ?string, direction?: string, startVertex?: string, graph?: ?string, edgeCollection?: array|string|null, minDepth?: int|null, maxDepth?: int|null, prune?: string|array|null, options?: array|object|string|null} $init = [] ][, array<string|int, mixed>|null &$binds = null ]) : string
This helper constructs the canonical Arango Query Language (AQL) traversal expression, optionally including depth ranges, edge collections or graph names, direction, and bind variables.
It supports flexible initialization via the $init array, automatic bind variable injection,
and seamless hydration of traversal options via TraversalOptions.
🧩 Canonical Form
FOR <vertexRef>, <edgeRef>, <pathRef>
IN <minDepth>..<maxDepth> <direction> <startVertex>
GRAPH <graphName>
OPTIONS { ... }
or, when using edge collections instead of a graph:
FOR <vertexRef>, <edgeRef>, <pathRef>
IN <minDepth>..<maxDepth> <direction> <startVertex>
<edgeCollection1>, <edgeCollection2>, ...
🔒 Bind Variables
If $binds is provided, both AQL::GRAPH and AQL::START_VERTEX
(and optionally AQL::EDGE_COLLECTION) are automatically bound using aqlBind(),
ensuring safe, injection-free query generation.
Example of secure binding:
$binds = [];
$aql = aqlTraversal([
AQL::GRAPH => 'socialGraph',
AQL::START_VERTEX => '@start',
AQL::DIRECTION => Traversal::INBOUND
], $binds);
print_r($binds);
// ['@start' => 'users/123', '@graph' => 'socialGraph']
💡 Usage Examples
1 - Simple graph traversal
echo aqlTraversal
([
AQL::GRAPH => 'socialGraph',
AQL::START_VERTEX => 'users/123',
]);
// FOR vertex IN OUTBOUND 'users/123' GRAPH 'socialGraph'
2 - Traversal with edges and path references
echo aqlTraversal
([
AQL::VERTEX_REF => 'v',
AQL::EDGE_REF => 'e',
AQL::PATH_REF => 'p',
AQL::DIRECTION => Traversal::INBOUND,
AQL::GRAPH => 'organization',
AQL::START_VERTEX => 'employees/42',
]);
// FOR v, e, p IN INBOUND 'employees/42' GRAPH 'organization'
3 - Depth-limited traversal
echo aqlTraversal
([
AQL::GRAPH => 'socialGraph',
AQL::START_VERTEX => 'users/123',
AQL::MIN_DEPTH => 1,
AQL::MAX_DEPTH => 3,
]);
// FOR vertex IN 1..3 OUTBOUND 'users/123' GRAPH 'socialGraph'
4 - Traversal using multiple edge collections
echo aqlTraversal
([
AQL::EDGE_COLLECTION => ['follows', 'likes'],
AQL::START_VERTEX => 'users/123',
AQL::DIRECTION => Traversal::OUTBOUND,
]);
// FOR vertex IN OUTBOUND 'users/123' follows, likes
5 - With PRUNE condition
echo aqlTraversal
([
AQL::GRAPH => 'socialGraph',
AQL::START_VERTEX => 'users/123',
AQL::PRUNE => 'vertex.age < 18',
]);
// FOR vertex IN OUTBOUND 'users/123' GRAPH 'socialGraph' PRUNE vertex.age < 18
6 - With OPTIONS
echo aqlTraversal
([
AQL::GRAPH => 'companyGraph',
AQL::START_VERTEX => 'departments/1',
AQL::OPTIONS => ['bfs' => true, 'uniqueVertices' => 'global'],
]);
// FOR vertex IN OUTBOUND 'departments/1' GRAPH 'companyGraph' OPTIONS { "bfs": true, "uniqueVertices": "global" }
Parameters
- $init : array{vertexRef?: string, edgeRef?: ?string, pathRef?: ?string, direction?: string, startVertex?: string, graph?: ?string, edgeCollection?: array|string|null, minDepth?: int|null, maxDepth?: int|null, prune?: string|array|null, options?: array|object|string|null} = []
-
Configuration for the traversal expression.
- $binds : array<string|int, mixed>|null = null
-
Optional reference to a bind variable array; used for safe variable substitution.
Tags
Return values
string —The generated AQL traversal clause, or an empty string if input is invalid.
aqlTraversalRange()
Builds an AQL traversal range clause (e.g., `1..1`, `1..5`, `..2`, `3..`).
aqlTraversalRange([int|null $minDepth = null ][, int|null $maxDepth = null ][, array<string|int, mixed>|null &$binds = null ][, string $defaultRange = Char::EMPTY ]) : string
Supports
- Fixed ranges (
1..1,2..5). - Open-ended ranges (
1..for "1 or more",..3for "up to 3"). - Bind parameters to avoid query plan cache invalidation.
AQL Syntax
FOR v, e, p IN 1..1 OUTBOUND ...
FOR v, e, p IN 1..5 OUTBOUND ...
FOR v, e, p IN ..3 OUTBOUND ... // 0 to 3
FOR v, e, p IN 2.. OUTBOUND ... // 2 or more
Why use bind parameters?
Using placeholders like @minDepth and @maxDepth allows ArangoDB to reuse
query execution plans, improving performance for repeated queries.
Examples
// Fixed range
echo aqlTraversalRange(1, 1);
// 1..1
// Open-ended max
echo aqlTraversalRange(1, null);
// 1..
// Open-ended min
echo aqlTraversalRange(null, 3);
// ..3
// With bind parameters
$binds = [];
echo aqlTraversalRange(1, 5, $binds);
// @minDepth..@maxDepth
// $binds = ['minDepth' => 1, 'maxDepth' => 5]
// With null parameters
echo aqlTraversalRange();
// ""
Parameters
- $minDepth : int|null = null
-
Minimum depth (inclusive). If null, no lower bound.
- $maxDepth : int|null = null
-
Maximum depth (inclusive). If null, no upper bound.
- $binds : array<string|int, mixed>|null = null
-
Optional reference to a binds array for parameterized queries.
- $defaultRange : string = Char::EMPTY
-
The default range if $minDepth=null && $maxDepth=null (Default "").
Tags
Return values
string —AQL traversal range (e.g., "1..1", "@minDepth..@maxDepth").
aqlUpdate()
Partially modifies a document with the given attributes, by adding new and updating existing attributes.
aqlUpdate([ReplaceOptions|null, with?: string|null} $init = [] ][, string $operation = Operation::UPDATE ]) : string
Basic Syntax:*
UPDATE `document` IN `collection`
UPDATE `keyExpression` WITH `document` IN `collection`
Parameters
- $init : ReplaceOptions|null, with?: string|null} = []
-
Initial options for the UPDATE or REPLACE statement, with the keys:
- 'collection' : The name of the collection in which the document should be updated.
- 'doc' : An object and contain the attributes and values to update.
- 'options' : The default 'options' expression definition
- 'rawValues' : array, keys whose values should be treated as raw AQL expressions - used with the 'with' option)
- 'rawKeys' : array, keys which should be kept raw (their values are not wrapped or converted) - used with the 'with' option)
- 'useSpace' : bool, add spaces around braces and after commas
- 'with' : One or multiple collections for WITH clause -> WITH collection1 [, collection2 [, ... collectionN ] ]
- $operation : string = Operation::UPDATE
-
The AQL operation to perform. Must be either Operation::UPDATE (default) or Operation::REPLACE.
If the "REPLACE" operation is used, see the replace method.
Tags
Return values
stringaqlUpsert()
Prepare the query to update an existing document, or creates a new document if it does not exist.
aqlUpsert([JsonSerializable|null} $init = [] ]) : string
UPSERT [ searchExpression | FILTER filterExpression ]
INSERT insertExpression
UPDATE updateExpression
IN collection
Options in $init :
- collection : The name of the collection
- filter : The alternative filterExpression, this syntax for UPSERT operations allows you to use more flexible filter conditions beyond equality matches to look up documents.
- search : The 'searchExpression' contains the document to be looked for. It must be an object literal (UPSERT {
: , ... } ...) without dynamic attribute names. In case no such document can be found in collection, a new document is inserted into the collection as specified in the insertExpression. - insert : The document to insert in the collection if the document not exist.
- update : The document to update in the collection.
- options : The optional upsert options definition array or object.
- return : optional expression to define the RETURN clause. Default is Clause::NEW. You can also use Clause::WITH_STATUS to return both the document and the type of operation.
Parameters
- $init : JsonSerializable|null} = []
-
Configuration options for the UPSERT query.
Tags
Return values
string —The generated AQL UPSERT query.
aqlVectorSearch()
Builds a complete AQL approximate nearest-neighbour (ANN) query over a vector index.
aqlVectorSearch(string $collection, string $attribute, string $vector, int $limit[, string $metric = VectorMetric::COSINE ][, int|null $nProbe = null ][, string $docRef = 'doc' ][, string|null $return = null ]) : string
The generated query follows the canonical ANN form:
FOR <docRef> IN <collection>
SORT APPROX_NEAR_<METRIC>(<docRef>.<attribute>, <vector>) <ASC|DESC>
LIMIT <limit>
RETURN <return>
The $metric selects both the AQL function and the sort direction, which
is the part developers get wrong most often:
'cosine'→APPROX_NEAR_COSINEsortedDESC(closer to 1 is nearer),'l2'→APPROX_NEAR_L2sortedASC(closer to 0 is nearer).
The metric must match the metric of the VectorIndex
covering $attribute, otherwise the optimiser cannot accelerate the query.
Requires ArangoDB started with the experimental vector index feature.
Example: cosine search with a bound query vector
use function oihana\arango\db\operations\aqlVectorSearch;
$aql = aqlVectorSearch
(
collection : 'items' ,
attribute : 'embedding' ,
vector : '@query' ,
limit : 10 ,
) ;
// FOR doc IN items SORT APPROX_NEAR_COSINE(doc.embedding,@query) DESC LIMIT 10 RETURN doc
Example: L2 search, custom nProbe, projection and iteration variable
$aql = aqlVectorSearch
(
collection : 'items' ,
attribute : 'embedding' ,
vector : '@query' ,
limit : 5 ,
metric : 'l2' ,
nProbe : 20 ,
docRef : 'd' ,
return : '{ key: d._key, score: APPROX_NEAR_L2(d.embedding, @query) }' ,
) ;
// FOR d IN items SORT APPROX_NEAR_L2(d.embedding,@query,{"nProbe":20}) ASC LIMIT 5
// RETURN { key: d._key, score: APPROX_NEAR_L2(d.embedding, @query) }
Parameters
- $collection : string
-
The collection to scan (or any AQL iterable expression).
- $attribute : string
-
The document attribute holding the indexed vector (e.g.
'embedding'). - $vector : string
-
The query vector — typically a bind placeholder (
'@query') or an AQL array literal. - $limit : int
-
The number of nearest neighbours to return (the
LIMIT). - $metric : string = VectorMetric::COSINE
-
The similarity metric:
'cosine'(default) or'l2'. Must match the vector index. - $nProbe : int|null = null
-
Optional number of neighbouring centroids to probe (higher = more accurate, slower).
- $docRef : string = 'doc'
-
The iteration variable name (default
'doc'). - $return : string|null = null
-
Optional
RETURNexpression. Defaults to the iteration variable (the whole document).
Tags
Return values
string —The complete AQL ANN query.
aqlWindow()
Builds an AQL `WINDOW` clause for sliding-window aggregation (running totals, rolling averages, and other statistical properties over related rows).
aqlWindow([array<string|int, mixed> $init = [] ]) : string
Two forms are supported, selected by the presence of AQL::RANGE_VALUE:
Row-based (a fixed number of adjacent rows) — no rangeValue:
WINDOW { preceding: numPrecedingRows, following: numFollowingRows }
AGGREGATE variableName = aggregateExpression
Range-based (a value or duration range around rangeValue) — with rangeValue:
WINDOW rangeValue WITH { preceding: offsetPreceding, following: offsetFollowing }
AGGREGATE variableName = aggregateExpression
The WITH keyword here belongs to the range-based WINDOW syntax and is
unrelated to the collection-declaring WITH operation (aqlWith()).
Supported $init keys
| Key | Type | Description |
|---|---|---|
AQL::AGGREGATE |
array | Aggregation expressions, e.g. ['rollingAvg' => 'AVG(doc.val)']. Required. |
AQL::PRECEDING |
int | float |
AQL::FOLLOWING |
int | float |
AQL::RANGE_VALUE |
string | The row-value expression for a range-based window (e.g. 'doc.time'). When set, the range-based form is emitted. |
Bound values are serialized as-is when numeric and single-quoted when given as
strings (so ISO 8601 durations like PT1H / P1Y6M are emitted as 'PT1H').
A bound that is null is omitted from the { … } object.
For a running total (aggregate every row from the start up to the current one),
use the string 'unbounded' as the preceding bound — e.g.
[ AQL::PRECEDING => 'unbounded' , AQL::FOLLOWING => 0 , … ] yields
WINDOW { preceding: 'unbounded', following: 0 } AGGREGATE ….
Examples
Row-based rolling average (previous, current, next row):
echo aqlWindow
([
AQL::PRECEDING => 1 ,
AQL::FOLLOWING => 1 ,
AQL::AGGREGATE => [ 'rollingAvg' => 'AVG(doc.val)' ] ,
]);
// WINDOW { preceding: 1, following: 1 } AGGREGATE rollingAvg = AVG(doc.val)
Range-based sum over a duration window:
echo aqlWindow
([
AQL::RANGE_VALUE => 'doc.time' ,
AQL::PRECEDING => 'PT1H' ,
AQL::FOLLOWING => 0 ,
AQL::AGGREGATE => [ 'total' => 'SUM(doc.val)' ] ,
]);
// WINDOW doc.time WITH { preceding: 'PT1H', following: 0 } AGGREGATE total = SUM(doc.val)
Parameters
- $init : array<string|int, mixed> = []
-
Associative array of window options.
Tags
Return values
string —The compiled AQL WINDOW clause, or an empty string when no
aggregate is supplied (a WINDOW without aggregation is meaningless).
aqlWindowBounds()
Serializes the `{ preceding: …, following: … }` bounds object of a `WINDOW` clause.
aqlWindowBounds(int|float|string|null $preceding, int|float|string|null $following) : string
Numeric bounds are emitted bare; string bounds are single-quoted (ISO 8601
durations such as PT1H, or the 'unbounded' keyword). A null bound is
omitted from the object.
echo aqlWindowBounds( 1 , 1 ) ; // { preceding: 1, following: 1 }
echo aqlWindowBounds( 'unbounded' , 0 ) ; // { preceding: 'unbounded', following: 0 }
echo aqlWindowBounds( 0 , null ) ; // { preceding: 0 }
echo aqlWindowBounds( null , null ) ; // { }
Parameters
- $preceding : int|float|string|null
-
Lower window bound.
- $following : int|float|string|null
-
Upper window bound.
Tags
Return values
string —The bounds object literal, e.g. { preceding: 1, following: 1 }.
aqlWith()
Generates an AQL `WITH` clause for one or more collections.
aqlWith(string ...$collections) : string
The WITH clause restricts the query to only access the specified collections.
Syntax:
WITH collection1, collection2, ...
Example usage:
echo aqlWith('users'); // "WITH users"
echo aqlWith('users', 'orders'); // "WITH users, orders"
echo aqlWith(); // ""
Parameters
- $collections : string
-
List of collection names to include in the query.
Tags
Return values
string —The generated AQL WITH clause, or an empty string if none provided.