Skip to content

feat(promql): lower group() and count_values() aggregation operators (#49) - #87

Merged
zzylol merged 2 commits into
mainfrom
feat/49-group-count-values
Jul 3, 2026
Merged

zzylol merged 2 commits into
mainfrom
feat/49-group-count-values

Conversation

@zzylol

@zzylol zzylol commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Closes #49. Splits the sampling operators limitk/limit_ratio to #86.

What

group, count_values, limitk, limit_ratio all parsed but were rejected as UnsupportedAggregateOp. This lands the two genuine aggregation operators; the two sampling operators are split out (they need a selection node, not an aggregate intent).

How

L3 (asap-ir)

  • AggIntent::Group — a constant 1 per group ("group presence"). Distinct from Sum/Count — its value is always 1 regardless of inputs. (group was previously mislowered onto Sum.)
  • AggIntent::CountValues { label } — group the input series by their sample value, count each distinct value, and emit that value as a new label. Unlike every other reducer it adds a synthesized Utf8 label column, so Aggregate schema derivation special-cases it (two output columns: the label + the count) and drops the by-keys-only unique key. Both intents added to agg_is_exact.

L2 (asap-l2)

  • AggFunc::Group / AggFunc::CountValues { label } + converter arms.

Front end (asap-frontend-promql)

  • T_GROUPOuterIntent::Group; T_COUNT_VALUESOuter::CountValues carrying the string label param (str_param, which unwraps nested parens: count_values((("v")), m)). Both route through the existing two-level build / build_over_subtree machinery, so group by (job) / count_values by (job) grouping works.

Scope: limitk / limit_ratio deferred to #86

These are series-sampling selection operators — they return a deterministic-but-unordered subset of the input series unchanged, not a reduction. Modeling limitk as Sort{value} → Limit{k} (topk) would change which series pass through — a silent divergence the corpus actively checks (count(limitk(2, http_requests) and http_requests)). limit_ratio selects a fraction of a runtime-unknown series count and often carries a dynamic ratio. Left rejected, pinned by limitk_and_limit_ratio_are_rejected__GAP, design in #86.

without(...) grouping forms (count_values without (instance)(…), group without(point)(…)) remain rejected by the existing usage-derived-schema limitation — unchanged here.

Tests

Conformance §S:

  • group(up)Group intent, group output column; group by (job) keeps the key; regression guard that it is not a Sum.
  • count_values("version", build_version)CountValues{version}; output schema gains a Utf8 version label + a count column.
  • count_values by (job) ((("v")), m) — nested-paren label + by grouping.
  • limitk / limit_ratio GAP.

Flipped the old group_aggregator_is_rejected conformance pin and the group_is_not_sum equivalence pin from rejectdistinct-intent.

Full workspace suite green; clippy clean.

zz_y and others added 2 commits July 3, 2026 12:51
…49)

`group`, `count_values`, `limitk`, `limit_ratio` all parsed but were rejected
as UnsupportedAggregateOp. Add the two genuine *aggregation* operators; the
sampling operators are split to #86 (they need a selection node, not an
aggregate intent).

L3 (asap-ir):
- `AggIntent::Group` — constant 1 per group ("group presence"). Distinct from
  Sum/Count (its value is always 1, independent of inputs); previously `group`
  was mislowered onto `Sum`.
- `AggIntent::CountValues { label }` — group the input series by their sample
  value, count each distinct value, and emit the value as a new label. Unlike
  every other reducer it adds a synthesized Utf8 label column, so `Aggregate`
  schema derivation special-cases it (two output columns: the label + count),
  and drops the by-keys-only unique key. Both added to `agg_is_exact`.

L2 (asap-l2):
- `AggFunc::Group` / `AggFunc::CountValues { label }` + converter arms.

Front end (asap-frontend-promql):
- `T_GROUP` → `OuterIntent::Group`; `T_COUNT_VALUES` → `Outer::CountValues`
  carrying the string label parameter (`str_param`, which unwraps nested
  parens: `count_values((("v")), m)`). Both flow through the existing two-level
  `build` / `build_over_subtree` machinery.

`limitk`/`limit_ratio` stay rejected: they are series-*sampling* selection
(return an unordered subset of series unchanged), so modeling them as topk
would change which series pass through. Split to #86, pinned by
`limitk_and_limit_ratio_are_rejected__GAP`.

Tests: conformance §S (group intent + keys, count_values value→label + count,
parenthesised label + by-grouping, limitk/limit_ratio GAP). Flipped the old
`group_aggregator_is_rejected` pin and the `group_is_not_sum` equivalence pin
(reject → distinct).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`count_values by (job)("job", v)` names the synthesized value-label the same
as a group-by key, which emitted a duplicate `job` column in the output
schema. PromQL's synthesized label takes precedence, so emit a single column:
skip the synthesized label when a column of that name already exists.

Found in self-review of #49.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@zzylol
zzylol merged commit 789af35 into main Jul 3, 2026
1 check passed
@zzylol
zzylol deleted the feat/49-group-count-values branch July 3, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PromQL: extended aggregation operators (limitk/limit_ratio/count_values/group) unsupported

1 participant