BoundsQueryTrait uses trait:short
Builds the AQL query that computes the numeric **bounds** — the `{ min, max, count }` extent — of several fields at once, alongside (not replacing) the document list.
Each requested field is a key of the model's $this->bounds whitelist. The
extent is aggregated over the same conjunctive filter as the list, so the
bounds frame the currently filtered set (a ?bounds= request scoped by
?filter= / ?search / a facet narrows to that subset). Alongside the two
scalars, count reports how many values framed the extent (post-exclusion,
non-null) — so a client knows whether a range control is worth showing.
Flat scalar fields share a single COLLECT AGGREGATE — one pass over the
filtered set frames every one of them at once:
FOR doc IN @@coll FILTER <same filters>
COLLECT AGGREGATE width_min = MIN(doc.width), width_max = MAX(doc.width), width_count = SUM(doc.width != null ? 1 : 0)
RETURN { width: { min: width_min, max: width_max, count: width_count } }
A nested measure declared with the [*] array-expansion marker
(offers[*].price) cannot share that root FOR — it must unwind the array —
so it gets its own LET sub-query, merged into the flat block.
Exclusions. A bound definition can drop values from the aggregate through
Bound options (POSITIVE → > 0, MIN / MAX → accepted domain,
IGNORE → sentinel value(s)). An excluded value is mapped to null — which
MIN / MAX already ignore — so the guard is per field: a document
dropped from one field's extent still frames the others.
COLLECT AGGREGATE width_min = MIN(doc.width > 0 ? doc.width : null), … , width_count = SUM(doc.width > 0 ? 1 : 0)
Tags
Table of Contents
Constants
- BOUND_CNT : string = 'cnt'
- The count aggregate variable of a nested sub-query.
- BOUND_COUNT : string = 'count'
- The count attribute name in the returned `{ min, max, count }` object.
- BOUND_HI : string = 'hi'
- The upper-bound aggregate variable of a nested sub-query.
- BOUND_ITEM : string = 'item'
- The unwind loop variable for nested ([*]) bound fields.
- BOUND_LO : string = 'lo'
- The lower-bound aggregate variable of a nested sub-query.
- BOUND_MAX : string = 'max'
- The upper-bound attribute name in the returned `{ min, max, count }` object.
- BOUND_MIN : string = 'min'
- The lower-bound attribute name in the returned `{ min, max, count }` object.
- BOUND_NULL : string = 'null'
- The AQL `null` literal — the value an excluded measure maps to.
- BOUNDS_FLAT : string = '__bounds'
- The `LET` variable holding the flat-fields extent object, merged with the nested-field sub-queries.
Methods
- buildBoundsQuery() : string
- Builds the bounds query, or an empty string when nothing is boundable.
- buildFilteredScope() : array{0: string, 1: ?string}
- Builds the `FOR` segment and the conjunctive `FILTER` clause shared by the list-family queries.
- assembleBoundsQuery() : string
- Assembles the final query from the flat block and the nested `LET`s.
- boundCondition() : string|null
- Builds the AQL boolean condition a value must satisfy to enter the extent, from the declaration's exclusion options, or null when none is declared.
- boundCount() : string
- The count expression fed to `SUM`: `1` for each value that frames the extent (non-null, and passing the exclusion condition when declared), `0` otherwise — so `count` reports how many values the `{ min, max }` spans.
- boundObject() : string
- Serializes a `{ min: <lo>, max: <hi>, count: <count> }` object from three AQL variables.
- boundValue() : string
- The value expression fed to `MIN` / `MAX`: the raw reference when no exclusion is declared (`MIN` / `MAX` ignore `null` natively), or a `<condition> ? <reference> : null` guard that maps an excluded value to `null` so it does not skew the extent.
- buildBoundsSubquery() : string
- Builds one nested ([*]) field's extent sub-query, unwinding each `[*]` hop with a `FOR` and aggregating `MIN` / `MAX` / count over the projected leaf.
- normalizeBounds() : array<string, array<string|int, mixed>>
- Normalizes the `$bounds` whitelist to a `field => definition` map.
Constants
BOUND_CNT
The count aggregate variable of a nested sub-query.
private
string
BOUND_CNT
= 'cnt'
BOUND_COUNT
The count attribute name in the returned `{ min, max, count }` object.
private
string
BOUND_COUNT
= 'count'
BOUND_HI
The upper-bound aggregate variable of a nested sub-query.
private
string
BOUND_HI
= 'hi'
BOUND_ITEM
The unwind loop variable for nested ([*]) bound fields.
private
string
BOUND_ITEM
= 'item'
BOUND_LO
The lower-bound aggregate variable of a nested sub-query.
private
string
BOUND_LO
= 'lo'
BOUND_MAX
The upper-bound attribute name in the returned `{ min, max, count }` object.
private
string
BOUND_MAX
= 'max'
BOUND_MIN
The lower-bound attribute name in the returned `{ min, max, count }` object.
private
string
BOUND_MIN
= 'min'
BOUND_NULL
The AQL `null` literal — the value an excluded measure maps to.
private
string
BOUND_NULL
= 'null'
BOUNDS_FLAT
The `LET` variable holding the flat-fields extent object, merged with the nested-field sub-queries.
private
string
BOUNDS_FLAT
= '__bounds'
Methods
buildBoundsQuery()
Builds the bounds query, or an empty string when nothing is boundable.
public
buildBoundsQuery([array<string|int, mixed> $init = [] ][, array<string|int, mixed> &$bindVars = [] ][, string $docRef = AQL::DOC ]) : string
Parameters
- $init : array<string|int, mixed> = []
-
The list query options (
Arango::BOUNDSholds the fields). - $bindVars : array<string|int, mixed> = []
-
The bind variables, populated by reference.
- $docRef : string = AQL::DOC
-
The document reference.
Tags
Return values
string —The compiled AQL query, or an empty string.
buildFilteredScope()
Builds the `FOR` segment and the conjunctive `FILTER` clause shared by the list-family queries.
protected
buildFilteredScope([array<string|int, mixed> $init = [] ][, array<string|int, mixed> &$bindVars = [] ]) : array{0: string, 1: ?string}
Parameters
- $init : array<string|int, mixed> = []
-
The query options.
- $bindVars : array<string|int, mixed> = []
-
The bind variables, populated by reference.
Tags
Return values
array{0: string, 1: ?string} —The [ $for, $filter ] pair.
assembleBoundsQuery()
Assembles the final query from the flat block and the nested `LET`s.
private
assembleBoundsQuery(string $for, string|null $filter, array<string|int, mixed> $aggregate, array<string|int, mixed> $flatReturn, array<string|int, mixed> $lets, array<string|int, mixed> $mergeParts) : string
Three shapes, cheapest first: a single flat block becomes the top-level
query directly; nested-only fields return an object of their LETs; a mix
binds the flat block to a LET and MERGEs the nested fields into it.
Parameters
- $for : string
-
The shared
FORsegment. - $filter : string|null
-
The shared
FILTERclause. - $aggregate : array<string|int, mixed>
-
The flat
COLLECT AGGREGATEassignments. - $flatReturn : array<string|int, mixed>
-
The flat
RETURNentries. - $lets : array<string|int, mixed>
-
The nested-field
LETclauses. - $mergeParts : array<string|int, mixed>
-
The nested-field
RETURNentries.
Tags
Return values
string —The compiled AQL query, or an empty string when nothing was boundable.
boundCondition()
Builds the AQL boolean condition a value must satisfy to enter the extent, from the declaration's exclusion options, or null when none is declared.
private
boundCondition(string $reference, array<string|int, mixed> $definition) : string|null
Conditions combine with a logical AND: POSITIVE → > 0, MIN / MAX →
the accepted [ min, max ] domain, IGNORE → NOT IN [ … ] sentinels.
Parameters
- $reference : string
-
The value reference (e.g.
doc.width). - $definition : array<string|int, mixed>
-
The bound definition.
Tags
Return values
string|null —The AQL condition, or null when the value is unfiltered.
boundCount()
The count expression fed to `SUM`: `1` for each value that frames the extent (non-null, and passing the exclusion condition when declared), `0` otherwise — so `count` reports how many values the `{ min, max }` spans.
private
boundCount(string $reference, array<string|int, mixed> $definition) : string
Parameters
- $reference : string
-
The value reference.
- $definition : array<string|int, mixed>
-
The bound definition.
Tags
Return values
string —The count expression.
boundObject()
Serializes a `{ min: <lo>, max: <hi>, count: <count> }` object from three AQL variables.
private
boundObject(string $lo, string $hi, string $count) : string
Parameters
- $lo : string
-
The lower-bound variable / expression.
- $hi : string
-
The upper-bound variable / expression.
- $count : string
-
The count variable / expression.
Tags
Return values
string —The { min: <lo>, max: <hi>, count: <count> } object literal.
boundValue()
The value expression fed to `MIN` / `MAX`: the raw reference when no exclusion is declared (`MIN` / `MAX` ignore `null` natively), or a `<condition> ? <reference> : null` guard that maps an excluded value to `null` so it does not skew the extent.
private
boundValue(string $reference, array<string|int, mixed> $definition) : string
Parameters
- $reference : string
-
The value reference.
- $definition : array<string|int, mixed>
-
The bound definition.
Tags
Return values
string —The value expression.
buildBoundsSubquery()
Builds one nested ([*]) field's extent sub-query, unwinding each `[*]` hop with a `FOR` and aggregating `MIN` / `MAX` / count over the projected leaf.
private
buildBoundsSubquery(string $property, array<string|int, mixed> $definition, string $for, string|null $filter, string $docRef) : string
Parameters
- $property : string
-
The
[*]-bearing property path. - $definition : array<string|int, mixed>
-
The bound definition (exclusion options).
- $for : string
-
The pre-built root
FORsegment. - $filter : string|null
-
The shared
FILTERclause. - $docRef : string
-
The document reference.
Tags
Return values
string —The FOR … COLLECT AGGREGATE … RETURN { min, max, count } sub-query.
normalizeBounds()
Normalizes the `$bounds` whitelist to a `field => definition` map.
private
normalizeBounds() : array<string, array<string|int, mixed>>
Accepts both notations, mixed in the same declaration:
- a bare field name (list entry, e.g.
'width') → an empty definition; - a keyed definition (
'price' => [ Bound::PROPERTY => '…' ]) → itself (a non-array value, e.g.'width' => true, becomes an empty definition).
The sole caller (buildBoundsQuery()) already guarantees $this->bounds
is an array, so no defensive is_array guard is needed here.
Return values
array<string, array<string|int, mixed>> —The normalized whitelist (empty when nothing boundable).