GROUP BY, which groups rows sharing the same value in one or more fields before the aggregation is applied.
Aggregate vs. scalar functions
Reference
Basic aggregation example
DISTINCT support
Passingtrue as the second argument (Fluent API) or prefixing the field with DISTINCT (FQL) causes the function to operate only on unique values, ignoring duplicates.
DISTINCT is not supported with COUNT(*). Using it will throw an InvalidArgumentException.HAVING clause
HAVING filters groups after aggregation, similar to how WHERE filters individual rows before aggregation. You can reference aggregated aliases in the HAVING condition.
COUNT(*) vs COUNT(field)
COLLECT_OBJECT
Added in 3.1.0.
COLLECT_OBJECT collapses every row in a GROUP BY group into a single array of structured objects. Each row is projected through an inner mini-SELECT (with field aliases and arithmetic), and the resulting list can optionally be sorted with an inner ORDER BY.
Grammar
- Inner items accept the full FQL expression syntax — column references, scalar functions (
CONCAT,ROUND,IF,COALESCE,UPPER,LOWER, …), arithmetic (price * 1.21 AS price_with_vat), and aliases. - The optional inner
ORDER BYaccepts multiple sort keys withASC/DESCand can reference both source columns and the aliases declared insideCOLLECT_OBJECT(...). - The aggregate returns
array<array<string, mixed>>per group.
FQL example
Fluent API
Build the inner projection withFQL\Query\Builder\CollectObject, then pass it to collectObject() and alias the result with as():
CollectObject::select() accepts the full FQL expression syntax — inline "expr AS alias" and comma-separated lists both work. orderBy(), asc(), and desc() chain just like on the main query.
Notes & limitations
- Empty groups produce no output row, consistent with the other aggregates.
- Null values propagate into the produced objects — unlike
SUM/AVG, which skip them. - Stable sort preserves accumulation order on ties.
- The inner
ORDER BYsees both source columns and the aliases declared insideCOLLECT_OBJECT(...). DISTINCT,LIMIT,WHERE, and nestedCOLLECT_OBJECTare not supported and will throw a clear exception.- Aggregates inside
COLLECT_OBJECTare accepted but rarely useful — they collapse the accumulated rows to a single output object, soCOLLECT_OBJECT(SUM(x))yields an array of length 1. Prefer scalar aggregates at the outer level alongsideCOLLECT_OBJECTfor per-group summary numbers.