GROUP BY divides the rows in the data stream into multiple groups, for each group keeps one row of data and discard the rest rows; it is always followed by an aggregation operation that generates an aggregated value for each group.
Syntax: GROUP BY<expression>
as <alias>
, <expression>
as <alias>
, ...
Input:
- <expression>: Grouping basis; multiple bases must be homologous and are operated from left to right
- <alias>: Alias of grouping basis, optional
n(as n1).re().n(as n2) as path
group by n1.shape, n2.color
return path, count(path)
In the UQL above, clause GROUP BY groups the one-step paths resulted from a template query, first by the shape of initial nodes n1, then by the colour of terminal nodes n2 in each group; count the number of paths in each group and return both path and the count.

ATTR
Example: Group all cards by card level, return the total number of cards at each level
find().nodes({@card}) as n
group by n.level as level
return level, count(n)
NODE
Example: Find cards held by Customer CU001, CU002, CU003, and return the array of Card IDs and their owners
n({_id in ["CU001","CU002","CU003"]} as n1)
.re({@has}).n({@card} as n2)
group by n1
return n1{*}, collect(n2._id)
Multi-level Grouping
Example: Find all-level customers' ownership of all-level cards, return customer levels, card levels, and the number of cards owned by each customer
n({@customer} as n1)
.re({@has}).n({@card} as n2)
group by n1.level as a, n2.level as b
return table(a, b, count(n2))