helpers
Table of Contents
Namespaces
Classes
- AltChain
- An `alt` transformation chain that remembers **who supplied it**.
Functions
- aqlArray() : string
- Convert a value to an AQL array expression.
- aqlAssignments() : string|null
- Builds a list of AQL assignments (key/value pairs) from an array.
- aqlDocument() : string
- Generate a document expression for ArangoDB AQL.
- aqlExpression() : string|null
- Converts a value into an AQL expression.
- aqlFields() : string|null
- Applies AQL filters to a set of fields and returns a string representation suitable for inclusion in an AQL query.
- aqlInsertExpression() : string
- Defines the basic 'INSERT' expression.
- aqlReplaceExpression() : string
- Defines the basic 'REPLACE' expression.
- aqlSafeArray() : string
- Wraps an AQL path in a safety check to ensure it resolves to an array.
- aqlSerialize() : string
- Serialize a value (array, object, scalar) into an AQL fragment.
- aqlUpdateExpression() : string
- Builds the `UPDATE` clause of an AQL operation.
- aqlUpsertExpression() : string
- Builds the leading clause of an AQL `UPSERT` operation.
- aqlValue() : string
- Transform a PHP value into an AQL-compatible expression.
- assertAttributeName() : void
- Asserts that a string is a safe AQL attribute name (or nested attribute path), throwing when it is not. This is the attribute-path counterpart of {@see assertBindVariable()}: use it before interpolating an untrusted identifier (e.g. a facet sub-field name from the URL) into a `doc.<name>` accessor, to guarantee no AQL injection is possible through the path.
- assertLanguageCode() : void
- Asserts that a value is a language code safe to interpolate into a query, throwing when it is not. The language counterpart of {@see assertAttributeName()}, and it exists for the same reason: a language tag names an attribute of the translations object, so it is written verbatim into the query string and can never be bound.
- assertVariableName() : void
- Asserts that a string is a safe AQL **variable** name, throwing when it is not.
- expandArrayPath() : array{0: string[], 1: string}
- Unwinds an array-expansion path into the chain of `FOR` hops AQL needs to walk it, plus the reference of the projected leaf.
- isAQLExpression() : bool
- Check if a string looks like an AQL expression that should not be quoted.
- isAQLFunction() : bool
- Check if a string is a valid AQL function call expression.
- isAQLId() : bool
- Checks if a value is a string matching the ArangoDB Document ID format (e.g., "collection/key").
- isAttributeName() : bool
- Checks whether a string is a safe AQL attribute name — or nested attribute path — that can be concatenated into a dot-notation accessor such as `doc.<name>` without any risk of AQL injection.
- isVariableName() : bool
- Tells whether a string is a safe AQL **variable** name — the identifier a `LET` binds, not a path to an attribute.
- matchesSkin() : bool
- Tests whether a `Field::SKINS` marker matches the active request skin.
- requestAlt() : mixed
- Reads an `alt` chain out of a **request slot**, presuming it came from the wire.
- resolveSkinFields() : mixed
- Resolves which projection an edge or join definition should use for the active request skin.
- resolveUpsertReturn() : mixed
- Resolves the `RETURN` expression of an upsert-family operation, expanding the {@see Clause::WITH_STATUS} shorthand into the ternary that reports which half of the upsert actually ran.
- stripArrayExpansion() : string
- Strips every array-expansion marker (`[*]`) from an attribute path, turning a query-side traversal path into the flat, dotted path used to *declare* an ArangoSearch link (or an inverted index).
- trustedAlt() : AltChain
- Signs an `alt` chain as authored by the consumer's own code, so its parameters are interpolated as written rather than bound.
- alterExpression() : string
- Apply an `alt` transformation chain to an arbitrary AQL expression.
- buildBetweenClauses() : string
- Assemble the AQL clauses of a `between` (range) comparison.
- buildCombinedInlineFilter() : string
- Build combined inline filter conditions for array expansion.
- buildInlineFilterCondition() : string
- Build inline filter condition for array expansion.
- resolveAltSides() : array{0: mixed, 1: mixed}
- Resolve the `alt` parameter into its key-side and value-side chains.
- resolveGeoPoint() : array{0: mixed, 1: mixed}
- Resolve a `[ latitude, longitude ]` pair from a request-supplied object.
- resolveQuantifier() : string
- Resolve the `quant` parameter into its AQL quantifier keyword.
- resolveTraversalQuantifier() : TraversalQuantifier
- Resolve the `quant` parameter for an edge/join traversal into the predicate decisions that shape its `LENGTH( FOR … RETURN 1 ) <cmp> <threshold>` check.
Functions
aqlArray()
Convert a value to an AQL array expression.
aqlArray([mixed $value = null ]) : string
This helper converts various PHP values to their AQL array representation. Objects are cast to arrays, arrays are JSON-encoded, strings are returned as-is, and other values return an empty array expression.
Parameters
- $value : mixed = null
-
Array, object, or string to convert.
Tags
Return values
string —AQL array expression.
aqlAssignments()
Builds a list of AQL assignments (key/value pairs) from an array.
aqlAssignments(array<string|int, mixed>|null $assignments[, string $separator = Char::COMMA . Char::SPACE ][, string $comparator = Operator::ASSIGN ]) : string|null
This is used for COLLECT ... ASSIGN, COLLECT ... AGGREGATE,
FOR ... UPDATE, FOR ... REPLACE, etc.
Parameters
- $assignments : array<string|int, mixed>|null
-
Array of assignments, e.g., ['key' => 'value'].
- $separator : string = Char::COMMA . Char::SPACE
-
Separator between pairs (e.g., ", ").
- $comparator : string = Operator::ASSIGN
-
Comparator between key and value (e.g., " = ").
Tags
Return values
string|nullaqlDocument()
Generate a document expression for ArangoDB AQL.
aqlDocument([object|array<string|int, mixed>|string|null $keyValues = [] ][, array<string|int, mixed> $options = [] ]) : string
Accepts:
- associative arrays: ['key' => value, ...]
- indexed arrays of [key, value] pairs: [['key', value], ...]
- objects: converted to associative arrays
- strings: returned as-is inside braces
- null: returns '}'
Options can be passed as an associative array:
- 'useSpace' : bool, add spaces around braces and after commas
- 'rawValues' : array, keys whose values should be treated as raw AQL expressions
- 'rawKeys' : array, keys which should be kept raw (their values are not wrapped or converted)
Parameters
- $keyValues : object|array<string|int, mixed>|string|null = []
-
Array of key-value pairs, associative array, object, string, or null
- $options : array<string|int, mixed> = []
-
Optional settings: ['useSpace'=>bool, 'rawValues'=>array, 'rawKeys'=>array]
Tags
Return values
string —JS-like object expression for AQL
aqlExpression()
Converts a value into an AQL expression.
aqlExpression(object|string|array<string|int, mixed>|null $value) : string|null
Accepts either:
- A string representing a raw AQL expression, returned as-is.
- An array or object representing key-value pairs, converted into an AQL document object using aqlDocument().
null, which results in anullreturn value.
Internally, this function delegates to aqlDocument() when $value
is an array or object, and otherwise returns the raw string.
Examples:
echo aqlExpression( "FOR u IN users RETURN u" ) ; // "FOR u IN users RETURN u"
echo aqlExpression( ['name' => 'John', 'age' => 30] ) ; // "{name:'John',age:30}"
echo aqlExpression( [['status', 'active']] ) ; // "{status:'active'}"
echo aqlExpression( null ) ; // null
Parameters
- $value : object|string|array<string|int, mixed>|null
-
The value to convert into an AQL expression.
Tags
Return values
string|null —Returns the raw AQL expression or a converted document string,
or null if the input value is null.
aqlFields()
Applies AQL filters to a set of fields and returns a string representation suitable for inclusion in an AQL query.
aqlFields(array<string|int, mixed>|null $fields[, string $docRef = AQL::DOC ][, ContainerInterface|null $container = null ][, array<string|int, mixed> $init = [] ][, string|null $edgeRef = null ]) : string|null
This method iterates over the provided fields and applies the corresponding
filter function based on the Field::FILTER option for each field. The
generated expressions are then concatenated into a single string, separated
by ', '.
Supported filters include:
- Scalar fields: BOOL, INT, DATETIME, DEFAULT
- Special fields: TRANSLATE, DISTANCE, REVISION
- Document relations: EDGE, EDGE_SINGLE, EDGE_COUNT, JOIN, JOIN_ARRAY, JOIN_MULTIPLE, UNIQUE_NAME
Each field can also define additional options:
Field::NAME: The target field name in the document (optional)Field::UNIQUE: Unique variable name to use for the AQL expression (optional)Field::QUOTED: Double-quote the output label (for keys that are not bare identifiers, e.g."my-key": …). The attribute access is then reached with backticks (doc.my-key``), the valid AQL form — neverdoc."my-key". AField::NAMEstill overrides the source attribute (only the label is quoted).Field::REQUIRES: Optional permission subject(s) — when present and the request-scoped authorizer denies them, the field is dropped from the projection (read-side gating).Field::ALTERS: Optionalalttransformation chain wrapping the projected value (e.g.["trim","lower"]=>name: LOWER(TRIM(doc.name))). Applied only to the default scalar projection (key: doc.key); ignored on typed/structural filters (BOOL, DATETIME, EDGE, JOIN, …).Field::NULLABLE: Optional guard on aFilter::DOCUMENTprojection — the rebuilt object is emitted only when its source attribute really is an object, otherwise the field yieldsnull(orField::ELSE). Without it, a missing source rebuilds an object of nulls. Only valid on that filter (it throws elsewhere) — on aFilter::URLthe source is a scalar key, whichIS_OBJECT()never accepts; composes withField::WHEN.Field::WHEN: Optional condition guarding the projected value, compiled to a ternary against the current reference and paired withField::ELSE. Valid on the default scalar projection and on the two filters that fabricate a value with no guard of their own —Filter::DOCUMENT(an object rebuilt attribute by attribute) andFilter::URL(a link rebuilt withCONCAT(), which drops a missing key and yields a truncated address). It throws elsewhere.Field::SCOPE: Optional projection source —Scope::VERTEX(default) reads the field from$docRef,Scope::EDGEreads it from$edgeRef(the traversal edge). The edge scope is only valid inside an edge sub-query (where$edgeRefis provided) and only on filters that project from a reference; it throws otherwise.Scope::EDGEequalsAQL::EDGE, so both forms are interchangeable.
Parameters
- $fields : array<string|int, mixed>|null
-
Array of fields definitions to filter. The array keys are the field identifiers, and the values are arrays of options (filter, name, unique, quoted, requires). If null or empty, the method returns null.
- $docRef : string = AQL::DOC
-
The document reference to use in AQL expressions. Defaults to
AQL::DOC. - $container : ContainerInterface|null = null
-
The optional DI Container reference.
- $init : array<string|int, mixed> = []
-
Optional associative array definition.
- $edgeRef : string|null = null
-
The traversal edge reference, used to project fields flagged with
Field::SCOPE => Scope::EDGE. Only set inside an edge sub-query;nulleverywhere else.
Tags
Return values
string|null —A string containing the filtered fields as AQL expressions,
suitable for use in a RETURN or LET statement. Returns
null if the input $fields is null or empty.
aqlInsertExpression()
Defines the basic 'INSERT' expression.
aqlInsertExpression([array<string|int, mixed> $init = [] ]) : string
Parameters
- $init : array<string|int, mixed> = []
Tags
Return values
stringaqlReplaceExpression()
Defines the basic 'REPLACE' expression.
aqlReplaceExpression([array<string|int, mixed> $init = [] ]) : string
Parameters
- $init : array<string|int, mixed> = []
Tags
Return values
stringaqlSafeArray()
Wraps an AQL path in a safety check to ensure it resolves to an array.
aqlSafeArray(string $path[, string|null $default = null ][, bool $useParentheses = true ]) : string
Useful for FOR loops to prevent runtime errors when the source property is null.
Transforms doc.items into (IS_ARRAY(doc.items) ? doc.items : []).
Parameters
- $path : string
-
The AQL path to the property (e.g., "doc.offers").
- $default : string|null = null
-
The default value if the doc property is not an array (Default '[]').
- $useParentheses : bool = true
Tags
Return values
string —The secured AQL expression.
aqlSerialize()
Serialize a value (array, object, scalar) into an AQL fragment.
aqlSerialize(mixed $value[, bool $topLevel = true ]) : string
CustomRules:
- Strings are returned as-is (raw expression)
- Associative arrays => {key:value}
- Indexed arrays => [v1,v2,...]
- Objects => {key:value} using public properties
- JsonSerializable objects are jsonSerialized first
Parameters
- $value : mixed
- $topLevel : bool = true
Tags
Return values
stringaqlUpdateExpression()
Builds the `UPDATE` clause of an AQL operation.
aqlUpdateExpression([array<string|int, mixed> $init = [] ]) : string
Renders the document/expression supplied under the AQL::UPDATE key as
UPDATE <expression> (the expression goes through aqlExpression(),
so it accepts an object literal, a [key, value] pair list, or a raw
string). The AQL::UPDATE key is required: an init that omits it throws
an InvalidArgumentException.
Parameters
- $init : array<string|int, mixed> = []
-
Associative array with:
AQL::UPDATE: array|string — the update expression (required).
Tags
Return values
string —The UPDATE … clause.
aqlUpsertExpression()
Builds the leading clause of an AQL `UPSERT` operation.
aqlUpsertExpression([array<string|int, mixed> $init = [] ]) : string
The ArangoDB syntax accepts the lookup as either a search expression or a filter expression — never both:
UPSERT [ searchExpression | FILTER filterExpression ]
Accordingly, exactly one of the two $init keys must be supplied:
AQL::SEARCH— an object literal matched by equality, rendered asUPSERT { … }(see aqlExpression()).AQL::FILTER— a more flexible filter expression, rendered asUPSERT FILTER …(see aqlFilter()).
Supplying neither, or both at the same time, throws an
InvalidArgumentException.
Parameters
- $init : array<string|int, mixed> = []
-
Associative array with exactly one of:
AQL::SEARCH: array|string — the search document.AQL::FILTER: array|string — the filter expression.
Tags
Return values
string —The UPSERT … clause.
aqlValue()
Transform a PHP value into an AQL-compatible expression.
aqlValue(mixed $value[, array<string|int, mixed> $rawValues = [] ]) : string
Automatically detects AQL functions using pattern matching and treats them as raw expressions. Also supports manual raw value specification for edge cases.
String handling flow:
+--------------------------+
| Is $val a string? |
+-----------+--------------+
|
No
|--> return $val as-is or throw (non-string)
|
Yes
v
+--------------------------+
| Is $val in rawValues? |
+-----------+--------------+
|
Yes |--> return $val (raw)
|
No
v
+--------------------------+
| Matches AQL function? |
| CONCAT(...), DATE_NOW() |
+-----------+--------------+
|
Yes |--> return $val (raw)
|
No
v
+--------------------------+
| Matches AQL pattern? |
| doc.field, @bind, col/key|
+-----------+--------------+
|
Yes |--> return $val (raw)
|
No
v
+--------------------------+
| Regular string |
| Escape and quote |
| return "'val'" |
+--------------------------+
Parameters
- $value : mixed
-
The PHP value to transform
- $rawValues : array<string|int, mixed> = []
-
Optional list of specific values to treat as raw AQL expressions
Tags
Return values
string —AQL expression representing the value
assertAttributeName()
Asserts that a string is a safe AQL attribute name (or nested attribute path), throwing when it is not. This is the attribute-path counterpart of {@see assertBindVariable()}: use it before interpolating an untrusted identifier (e.g. a facet sub-field name from the URL) into a `doc.<name>` accessor, to guarantee no AQL injection is possible through the path.
assertAttributeName(mixed $value[, bool $fromRequest = false ]) : void
Parameters
- $value : mixed
-
The attribute name to validate.
- $fromRequest : bool = false
-
Whether the name was supplied by the request rather than declared in code.
Tags
assertLanguageCode()
Asserts that a value is a language code safe to interpolate into a query, throwing when it is not. The language counterpart of {@see assertAttributeName()}, and it exists for the same reason: a language tag names an attribute of the translations object, so it is written verbatim into the query string and can never be bound.
assertLanguageCode(mixed $value[, bool $fromRequest = false ]) : void
The predicate itself lives in oihana/php-controllers
(isLanguageCode()) — a language tag is not
an ArangoDB notion. What stays here is the part that is one: deciding who to
blame, below.
Who is blamed depends on where the tag came from, exactly as for an attribute
name. A declared default language (Arango::DEFAULT_LANG, the site or
model fallback) is the consumer's own code and no request can fix it: it
refuses with a plain ValidationException, answered with a 500. A
requested language (Arango::LANG, the ?lang= parameter) is told it
wrote something the API cannot read — a 400.
⚠ A controller already filters ?lang= against its supported languages
whitelist, so a request rarely reaches this guard. A consumer calling the
model directly bypasses that controller, which is precisely why the guard
lives here too.
Parameters
- $value : mixed
-
The language tag to validate.
- $fromRequest : bool = false
-
Whether the tag was supplied by the request rather than declared in code.
Tags
assertVariableName()
Asserts that a string is a safe AQL **variable** name, throwing when it is not.
assertVariableName(mixed $value) : void
The variable counterpart of assertAttributeName(), and not
interchangeable with it: an attribute name may be a path (address.city),
a variable name may not — LET address.city = … is a syntax error. Use this
one before interpolating a declared identifier into a LET.
Parameters
- $value : mixed
-
The variable name to validate.
Tags
expandArrayPath()
Unwinds an array-expansion path into the chain of `FOR` hops AQL needs to walk it, plus the reference of the projected leaf.
expandArrayPath(string $property, string $docRef[, string $itemRef = 'item' ]) : array{0: string[], 1: string}
It is the query-side counterpart of stripArrayExpansion(): where the
search links and the hierarchical ?filter= builder flatten the
Operator::ARRAY_EXPANSION marker into a dotted path, an aggregation has
to unwind it — an array element cannot be aggregated in place, it must be
iterated. 'offers[*].tiers[*].amount' becomes:
FOR item IN doc.offers
FOR item2 IN item.tiers
… item2.amount
The path is split on the marker, every segment but the last opening one hop
relative to the previous item reference, and the last segment being the
projected leaf — empty for a bare tags[*], which projects the element
itself. Each item reference is <itemRef>, then <itemRef>2, <itemRef>3 …
(there is no <itemRef>1).
Every segment is validated by assertAttributeName() — the whole path
never is, since it carries the markers — so a malformed path fails loud
instead of reaching AQL. A doubled marker (a[*][*]) yields an empty
intermediate container and is rejected on that ground.
The helper deliberately stops at the hops: the root FOR, the shared FILTER
and the aggregation tail belong to the caller, and diverge between consumers
(BoundsQueryTrait collects
MIN/MAX/count, FacetCountsQueryTrait
counts buckets).
Parameters
- $property : string
-
The
[*]-bearing attribute path. - $docRef : string
-
The document reference the first hop starts from.
- $itemRef : string = 'item'
-
The base name of the item variables (default
item).
Tags
Return values
array{0: string[], 1: string} —A [ fors , value ] pair: the FOR hops in
order, and the reference to project (the leaf, or the innermost item
when the path ends on a marker).
isAQLExpression()
Check if a string looks like an AQL expression that should not be quoted.
isAQLExpression(mixed $value) : bool
Detects:
- AQL functions: CONCAT(...), DATE_NOW(), etc.
- Document references: doc.field, user.name, etc.
- Bind parameters:
Parameters
- $value : mixed
-
The expression value to check
Tags
Return values
bool —True if it looks like an AQL expression
isAQLFunction()
Check if a string is a valid AQL function call expression.
isAQLFunction(string $expression) : bool
This function checks if the provided expression starts with a known AQL function name (case-insensitively) followed by parentheses.
Parameters
- $expression : string
-
The expression to check, e.g., 'COUNT(doc)'.
Tags
Return values
bool —True if it's a valid and known AQL function call.
isAQLId()
Checks if a value is a string matching the ArangoDB Document ID format (e.g., "collection/key").
isAQLId(mixed $value) : bool
This function validates the format, not whether the document actually exists. It specifically checks for a string containing exactly one '/' separator, with characters on both sides.
Parameters
- $value : mixed
-
The value to check.
Tags
Return values
bool —True if the value is a string in "collection/key" format, false otherwise.
isAttributeName()
Checks whether a string is a safe AQL attribute name — or nested attribute path — that can be concatenated into a dot-notation accessor such as `doc.<name>` without any risk of AQL injection.
isAttributeName(mixed $value) : bool
A valid name is one or more identifier segments joined by dots, where each
segment starts with a letter or underscore and continues with letters, digits
or underscores. This is exactly what AQL dot notation accepts unquoted, so any
character able to break out of an attribute path (spaces, operators, quotes,
parentheses, -, ;, …) is rejected.
It is the attribute-path counterpart of isBindVariable() (which guards bind variable names): use it whenever an untrusted identifier — e.g. a facet sub-field name coming from the URL — is interpolated into a query string.
Parameters
- $value : mixed
-
The value to check.
Tags
Return values
bool —True when $value is a safe single or dotted attribute name.
isVariableName()
Tells whether a string is a safe AQL **variable** name — the identifier a `LET` binds, not a path to an attribute.
isVariableName(mixed $value) : bool
The distinction matters, and isAttributeName() is the wrong guard for
this job: it validates a path, so it accepts address.city, which reads
perfectly as an attribute and is a syntax error as a variable
(LET address.city = …). A variable name is a single identifier: a letter or
an underscore, then letters, digits and underscores.
Two failures this cannot see, because they are not about shape:
- an AQL keyword (
LET,RETURN,FILTER, …) has the shape of an identifier and is refused by the server (expecting identifier); - a name already bound in the query —
docabove all — parses, then fails on execution (variable 'doc' is assigned multiple times).
Both are refused loudly by ArangoDB at the first query, with a message naming the variable, so they surface immediately rather than corrupting a result. Enumerating the keyword list here would only add a copy to keep in sync with the server, and a false sense of completeness.
Parameters
- $value : mixed
-
The candidate variable name.
Tags
Return values
bool —Whether it is a well-formed AQL variable name.
matchesSkin()
Tests whether a `Field::SKINS` marker matches the active request skin.
matchesSkin(mixed $skins, string|null $currentSkin) : bool
Used internally by the AQL projection layer (FieldsTrait::filterFieldsBySkin)
to decide whether a field declared with Field::SKINS => [...] should be
projected for the current ?skin= query parameter.
Accepted shapes for $skins :
null— no skin restriction declared, always matchesarray<string>— list of skins that activate the field, e.g.[ Skin::DEFAULT , Skin::FULL ]string— comma-separated list, e.g."main,full"
String comparisons are strict and trimmed of surrounding whitespace.
Parameters
- $skins : mixed
-
The
Field::SKINSvalue from the field definition. - $currentSkin : string|null
-
The active request skin, or
nullwhen no skin is set.
Tags
Return values
bool —true if the field must be kept in the projection, false to drop it.
requestAlt()
Reads an `alt` chain out of a **request slot**, presuming it came from the wire.
requestAlt(mixed $alt) : mixed
Called at the point where the chain is read, never later: that is the only place where the origin is still known. The presumption is deliberate — an unmarked chain in a request slot is treated as untrusted, so forgetting to sign one gives the safe behaviour (its parameters get bound) rather than a silent hole.
nullstaysnull— there is no chain to qualify.- A chain signed with trustedAlt() is unwrapped to its bare form, so it flows on exactly as it did before this mechanism existed.
- Anything else is wrapped as a request chain, which alterExpression() answers by binding its parameters — or by raising, when no binder was supplied.
Parameters
- $alt : mixed
-
The raw slot content.
Tags
Return values
mixed —null, the bare chain of a signed one, or an AltChain request wrapper.
resolveSkinFields()
Resolves which projection an edge or join definition should use for the active request skin.
resolveSkinFields(array<string|int, mixed> $definition, string|null $skin) : mixed
Resolution order :
AQL::SKIN_FIELDS[$skin]— explicit projection for this skinAQL::SKIN_FIELDS['*']— fallback bucket inside SKIN_FIELDSArango::FIELDS— legacy single projection (backwards-compatible)null— no projection declared at all
If AQL::SKIN_FIELDS is absent or not an array, the function ignores it
and falls back directly on Arango::FIELDS — definitions that pre-date
the SKIN_FIELDS feature keep their behaviour unchanged.
Parameters
- $definition : array<string|int, mixed>
-
The edge or join definition.
- $skin : string|null
-
The request-level skin (e.g. 'default', 'full').
Tags
Return values
mixed —The resolved projection (typically an array<string, mixed>) or null.
resolveUpsertReturn()
Resolves the `RETURN` expression of an upsert-family operation, expanding the {@see Clause::WITH_STATUS} shorthand into the ternary that reports which half of the upsert actually ran.
resolveUpsertReturn(array<string|int, mixed> $init, string $writeType) : mixed
An upsert either inserts or writes over an existing document, and the caller
usually cannot tell which from the returned document alone. WITH_STATUS
answers that by leaning on AQL's own signal: on an insert there is no OLD,
so the ternary reads
RETURN { doc: NEW , type: OLD ? 'replace' : 'insert' }
The write half is named by the caller, since it differs between the operations — aqlRepsert() overwrites the document (UpsertType::REPLACE) where aqlUpsert() merges into it (UpsertType::UPDATE). The insert half is always UpsertType::INSERT.
Any other AQL::RETURN value is passed through untouched, so a caller can
still return a hand-written expression; the default is Clause::NEW.
Parameters
- $init : array<string|int, mixed>
-
The operation options;
AQL::RETURNholds the requested expression. - $writeType : string
-
The upsert type reported when the document already existed.
Tags
Return values
mixed —The RETURN expression, expanded for WITH_STATUS and unchanged otherwise.
stripArrayExpansion()
Strips every array-expansion marker (`[*]`) from an attribute path, turning a query-side traversal path into the flat, dotted path used to *declare* an ArangoSearch link (or an inverted index).
stripArrayExpansion(string $path) : string
ArangoSearch (Community edition) indexes the sub-fields of array elements
natively when the link declares the sub-field without the [*] marker:
the server descends into the array on its own. The marker is only meaningful
in the AQL query (doc.contactPoints[*].email IN TOKENS(...)), never in the
link definition ({ fields : { contactPoints : { fields : { email : } } } } }).
This helper bridges the two surfaces by removing all the markers, so the same
declared path can build the link (stripped) and the query (kept).
It is the search/link counterpart of the [*] handling already performed by
the hierarchical ?filter= builder, reusing the same Operator::ARRAY_EXPANSION
marker. Every marker is removed, whatever the nesting depth — a multi-level
path such as employee[*].contactPoint[*].email flattens to a plain dotted
path (non-correlated search; correlation would require Enterprise nested
fields, out of scope here).
Parameters
- $path : string
-
The attribute path, possibly carrying
[*]markers.
Tags
Return values
string —The same path with every [*] marker removed.
trustedAlt()
Signs an `alt` chain as authored by the consumer's own code, so its parameters are interpolated as written rather than bound.
trustedAlt(mixed $chain) : AltChain
Only needed where a chain reaches the engine through a request slot — today
$init[FilterParam::ALT], which a host may also fill programmatically through
InjectFilterTrait::injectFilter(). Everything read from a model declaration
(Field::ALTERS, Facet::ALT) is trusted by default and needs no signature.
Sign a chain only when it must name an expression the request could not supply — another attribute, typically. A chain carrying plain values needs nothing: bound is what you want.
Parameters
- $chain : mixed
-
The chain, in any of the
altnotations.
Tags
Return values
AltChain —The signed chain.
alterExpression()
Apply an `alt` transformation chain to an arbitrary AQL expression.
alterExpression(string $expr, mixed $chain[, array<string|int, mixed> $init = [] ]) : string
Side-agnostic core shared by the key (left) and value (right) sides of a
comparison: it wraps $expr — whatever it is (a field reference doc.name, a
bind placeholder @value, or the loop variable CURRENT) — with the
function(s) described by $chain. Used directly by the filter and facet
builders (FilterTrait,
FacetTrait) and by the inline-condition
helpers (buildInlineFilterCondition()), so there is a single implementation.
Supports multiple syntax formats for $chain:
-
Single function: "lower" → LOWER(expr)
-
Function with params (simplified): ["substring", 0, 3] → SUBSTRING(expr, 0, 3)
-
Function chain: ["trim","lower"] → LOWER(TRIM(expr))
-
Mixed chain: ["trim",["substring",0,3],"lower"] → LOWER(SUBSTRING(TRIM(expr), 0, 3))
A name the catalogue does not carry is refused, not ignored. Every link is
checked against FilterFunction before it is applied, and the refusal names
the offending code — never a fragment of the query. Who wrote the chain decides
which refusal: a request can fix its own URL, so it gets a ValidationException
carrying 400; a model declaration cannot be fixed from the wire, so it gets an
UnsupportedOperationException, which surfaces as a 500 — the consumer's
code is what has to change.
⚠ One position cannot be checked, by construction. In ["trim","lowr","lower"]
the second element is read as a parameter of trim — exactly as it is in the
legitimate ["trim","-"] ("strip dashes"). The two notations are indistinguishable
there, so a name mistyped in that position stays silent: it becomes a parameter and
the rest of the chain is dropped. Write such a chain with each link nested —
["trim",["substring",0,3],"lower"] — and every link is checked again.
Parameters
- $expr : string
-
The expression to transform.
- $chain : mixed
-
The transformation chain (string, list of functions, or null for a no-op).
- $init : array<string|int, mixed> = []
-
Filter initialization array (forwarded to FilterFunction for boolean-return checks).
Tags
Return values
string —The transformed expression.
buildBetweenClauses()
Assemble the AQL clauses of a `between` (range) comparison.
buildBetweenClauses(string $left, string|null $min, string|null $max) : string
Given an already-resolved left operand and the (already-resolved) lower/upper bound expressions, builds the inclusive range test:
(left >= min && left <= max) // both bounds
left >= min // upper omitted (null)
left <= max // lower omitted (null)
Bound omission is the CALLER's policy: pass null for a bound to drop its
clause. Number/string filters drop the omitted side (one-sided range); date
filters resolve an omitted bound to "now" upstream, so they never pass null.
Returns an empty string when both bounds are null.
Parameters
- $left : string
-
The left operand (e.g.
doc.price,DATE_DAY(doc.d)). - $min : string|null
-
The lower-bound AQL expression, or null to omit it.
- $max : string|null
-
The upper-bound AQL expression, or null to omit it.
Tags
Return values
string —The combined range clause (parenthesized when both bounds are present).
buildCombinedInlineFilter()
Build combined inline filter conditions for array expansion.
buildCombinedInlineFilter(array<string|int, mixed> $match, array<string|int, mixed>|null &$binds[, array<string|int, mixed> $allowedFields = [] ][, mixed $alt = null ]) : string
Supports multiple logic operators:
- "all": ALL conditions must be true (AND logic)
- "any": AT LEAST ONE condition must be true (OR logic)
- "none": NO condition must be true (NOT logic)
Supports two formats:
-
Simple object (all conditions are "eq" with AND logic): {"propertyID": "X", "value": true} → CURRENT.propertyID == "X" AND CURRENT.value == true
-
Explicit array with operators and logic: {"all": [ {"key": "propertyID", "op": "eq", "val": "X"}, {"key": "value", "op": "ne", "val": null} ]} → CURRENT.propertyID == "X" AND CURRENT.value != null
{"any": [ {"key": "email", "op": "ne", "val": null}, {"key": "telephone", "op": "ne", "val": null} ]} → CURRENT.email != null OR CURRENT.telephone != null
{"none": [ {"key": "status", "op": "eq", "val": "deleted"}, {"key": "archived", "op": "eq", "val": true} ]} → !(CURRENT.status == "deleted" OR CURRENT.archived == true)
Parameters
- $match : array<string|int, mixed>
-
Match configuration
- $binds : array<string|int, mixed>|null
-
Bind variables array
- $allowedFields : array<string|int, mixed> = []
-
Optional: List of allowed field names for validation
- $alt : mixed = null
-
Optional
alttransformation applied to EVERY sub-field condition (field + value).
Tags
Return values
string —The combined inline filter condition
buildInlineFilterCondition()
Build inline filter condition for array expansion.
buildInlineFilterCondition(string $field, string $operator, mixed $value, array<string|int, mixed>|null &$binds[, mixed $alt = null ]) : string
Generates an AQL condition for use within array inline filtering syntax (CURRENT.field). Handles null values specially (no binding) and binds other values for security.
An optional $alt chain wraps the compared field (left, CURRENT.<field>)
and/or the bound value (right) — same alt:{key,val} / val:true mirror
vocabulary as the flat filters — so case-insensitive matches work inside the
array expansion (LOWER(CURRENT.email) == LOWER(@v)).
Parameters
- $field : string
-
The field name (e.g., "email")
- $operator : string
-
The comparison operator (e.g., "eq", "ne", "like")
- $value : mixed
-
The value to compare against
- $binds : array<string|int, mixed>|null
-
Bind variables array
- $alt : mixed = null
-
The
alttransformation (string/list = field only, object{key,val}= both sides); null for none.
Tags
Return values
string —The inline filter condition (e.g., "CURRENT.email != null")
resolveAltSides()
Resolve the `alt` parameter into its key-side and value-side chains.
resolveAltSides(mixed $alt) : array{0: mixed, 1: mixed}
Three backward-compatible forms are supported:
"lower"/["trim","lower"](string or list) → key side only, the value is left untouched.{ "key":<chain>, "val":<chain> }(object) → explicit chain per side.{ "key":<chain>, "val":true }→val:truemirrors the key-side chain onto the value side.
The object form is told apart from a plain function chain by being an associative array (a list is a function chain, an associative array is the per-side object). Shared by the filter and facet builders (FilterTrait, FacetTrait) and the inline-condition helpers.
Parameters
- $alt : mixed
-
The raw
altparameter.
Tags
Return values
array{0: mixed, 1: mixed} —A [ keyChain , valChain ] pair; either entry is null for a no-op on that side.
resolveGeoPoint()
Resolve a `[ latitude, longitude ]` pair from a request-supplied object.
resolveGeoPoint(mixed $value) : array{0: mixed, 1: mixed}
Reads the canonical Schema.org GeoCoordinates keys (latitude /
longitude) first, then falls back to the short aliases (lat / lng /
lon). Returns [ null, null ] when the value is not an array, or when a
coordinate is missing — callers treat that as "no geo point".
Parameters
- $value : mixed
-
The candidate object (typically a filter
valor a?near=payload).
Tags
Return values
array{0: mixed, 1: mixed} —The [ latitude, longitude ] pair.
resolveQuantifier()
Resolve the `quant` parameter into its AQL quantifier keyword.
resolveQuantifier(mixed $value) : string
The quant value answers the element axis of an array filter — « how many
elements must satisfy the condition » — independently of the comparator (which
lives in op). Three forms are accepted:
- a named quantifier
any/all/none→ANY/ALL/NONE(resolved through FilterQuantifier::getAlias()); - a bare integer
n(or its numeric string, e.g.3/"3") →AT LEAST (n). The threshold is cast to an int and inlined, which is injection-safe.
The returned keyword drives both array surfaces uniformly:
- scalar arrays via the array comparison operator (
doc.scores ALL >= @v); - object arrays via the question-mark operator (
doc.reviews[? ALL FILTER …]).
Parameters
- $value : mixed
-
The raw
quantparameter (any/all/noneor an integer).
Tags
Return values
string —The AQL quantifier keyword (ANY, ALL, NONE, AT LEAST (n)).
resolveTraversalQuantifier()
Resolve the `quant` parameter for an edge/join traversal into the predicate decisions that shape its `LENGTH( FOR … RETURN 1 ) <cmp> <threshold>` check.
resolveTraversalQuantifier(mixed $value) : TraversalQuantifier
This is the relation counterpart of resolveQuantifier() (which targets
the array surface). It shares the same vocabulary — any / all / none / an
integer n — but maps it to a count comparison rather than to an AQL
quantifier keyword:
any(or absent) →LENGTH(...) > 0with aLIMIT 1short-circuit (« at least one linked match ») — the historical, backward-compatible form;none→LENGTH(...) == 0withLIMIT 1(« no linked match »);all→LENGTH(...) == 0withLIMIT 1over the negated leaf (« no linked vertex violates the condition »); vacuously true with no relation. The caller negates the leaf and requires one to be present;- a bare integer
n(or its numeric string) →LENGTH(...) >= n, withoutLIMIT(the rows must be counted). The threshold is cast to an int and inlined, which is injection-safe — and consistent with the array surface.
n means « at least n » and must be >= 1: « at least 0 » is always true
(use none for « no linked match »).
Parameters
- $value : mixed
-
The raw
quantparameter (any,all,none, or an integer).
Tags
Return values
TraversalQuantifier —The resolved predicate decisions.