Skip to content

feat Expression group keys — temporal bucketing (trunc) for grouped aggregates #327

Description

@0x054

What to build

An expression-capable group key for aggregate projections, temporal first: a portable truncation surface — t.date.trunc("month") (or ferro.trunc("month", t.date)) — compiling to date_trunc on Postgres, usable as a projected field, group key, and order_by source exactly like a plain column.

Requested by Pinch (M8 reporting, PRD 0012, found at the M8 CP0 spike pinch-backend#46). Blocks nothing — recorded demand, not a gate: M8 ships on daily-grain GROUP BY t.date (verified working on 0.17.1) + Python folding, which is fine while fetched rows = days-with-activity. This retires that fold when key spaces grow, and is the first concrete consumer of the record plan's reserved per-field expr slot (ADR-0007/0009).

The gap, verified on 0.17.1

Group keys are columns only — plain or traversed. No date-part accessor or function surface exists in the query API (src/ferro/query/, aggregation guide). Wanted:

rows = await Transaction.select(
    lambda t: {
        "month": t.date.trunc("month"),   # ← does not exist
        "total": t.amount_minor.sum(),
    }
).where(lambda t: t.amount_minor < 0).all()

Acceptance criteria

  • trunc granularities day/week/month/quarter/year on date/datetime columns, both as projected field and group key; order_by composes.
  • Build-time rejection of granularities/types with no portable meaning (the guide's build-time-honesty stance).
  • Record-plan expr field shape exercised end to end; golden vectors extended.

Out of scope

General computed expressions, HAVING, window functions — temporal truncation is the whole ask.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions