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.