37 Groups
Many useful summaries do not need explicit grouping. When a fold appears inside tuple construction, it is evaluated relative to the current receiver.
For example, this expression counts enrolments for each course:
u.course.(
code,
enrolment.Σcount as enrolmentCount
)
There is no separate group by course clause. The current course already gives
the group: enrolment.Σcount counts the enrolments reached from that course.
Explicit grouping
Use explicit grouping when the groups are not already the current receiver. The following expression groups enrolments by course code:
u.enrolment.groupby(course.code)
This does not compute an aggregate. It groups the observed enrolments by the key
course.code. Aggregates are then written as folds over the group.
u.enrolment.groupby(course.code).(
code,
group.Σcount as enrolmentCount
)
The key course.code supplies the output code. The name group refers to
the enrolments in that group.
Naming the group
An explicit group label can make later path tails easier to read:
u.enrolment.group(e)by(course.code).(
code,
e.Σcount as enrolmentCount,
e.[current].Σcount as currentEnrolmentCount
)
Here e names the enrolments in each group. The second count counts all
enrolments for the course; the third count counts only the current enrolments.
Grouping by another key
The grouping key can navigate. This expression groups enrolments by department name:
u.enrolment.group(e)by(course.department.name).(
name as departmentName,
e.Σcount as enrolmentCount
)
Each group contains the enrolments whose courses are offered by the same department.
Grouping by several values
The key can be a tuple expression. This groups enrolments by department and by whether the enrolment is current:
u.enrolment.group(e)by(
(
course.department.name as departmentName,
current
)
).(
departmentName,
current,
e.Σcount as enrolmentCount
)
The tuple key gives one group for each department/current combination that is observed.
Relation to relational grouping
Explicit grouping is the path-expression counterpart of grouping before aggregation. In
Tutorial D, a summary query uses SUMMARIZE ... PER ... ADD .... In Predica,
grouping and aggregation remain separate path-expression steps:
u.enrolment.group(e)by(course.code).(
code,
e.Σcount as enrolmentCount
)
The group(e)by(course.code) part forms the groups. The e.Σcount part is the
aggregate over each group.
Groups and lists
Groups compose with Σlist. The following expression gives each department
with an ordered list of its course enrolment counts:
u.enrolment.group(e)by(course.department.name).(
name as departmentName,
e.groupby(course.code).Σlist(
(
code,
group.Σcount as enrolmentCount
)
order by code
) as courseEnrolments
)
The outer grouping forms department groups. Inside each department group, the enrolments are grouped again by course code and collected into an ordered list.
Composability
Grouping does not replace the other path-expression tools. It gives a new current group, and then the usual operations can be used inside that group: filters, tuple construction, folds, lists, alternatives and conditionals.
u.enrolment.group(e)by(course.code).(
code,
e.[current].Σcount as currentCount,
e.[¬current].Σcount as pastCount,
(if e.[current].Σexists then "active" else "inactive")
as status
)
The expression groups enrolments by course, computes two filtered counts, and then uses a conditional to label the course group.