pub struct Reduce {
pub input: NodeId,
pub storage: StorageId,
pub aggs: Vec<AggSpec>,
pub partition_key: Option<Vec<ColId>>,
pub eager: bool,
pub schema: Schema,
pub output: Cell<Option<OutEdge>>,
/* private fields */
}Expand description
Reduce: an invertible aggregate, global or grouped. The running accumulator(s)
live in Graph storage under Reduce::storage, not in the struct, so they
survive across fetch/push.
Fields§
§input: NodeIdUpstream input operator (the source connection, or a sub-pipeline tail).
storage: StorageIdScratch-state slot holding the per-group StorageValue::Reduce counters.
aggs: Vec<AggSpec>The aggregates this node computes, in output-column order. v1: [Count].
partition_key: Option<Vec<ColId>>The grouping columns, in input-row coordinates. None = global (a single
immortal row); Some = one row per distinct value-tuple (top-level GROUP BY).
In the output row the group columns occupy positions 0..partition_key.len().
eager: boolHydration regime for the grouped case (REDUCE-DESIGN.md §8.1; moot when
partition_key is None). true = eager (top-level GROUP BY → View,
full-fold hydrate; an Add to a no-state group births it, death deletes the
slot). false = lazy (relationship aggregate → per-group constrained
fetch; an Add to a no-state group is dropped and folded on the next fetch,
death keeps the count-0 slot so the next Add re-births).
schema: SchemaThe synthetic output schema (REDUCE-DESIGN.md §3): [group_cols…, agg_cols…]. Global ⇒ just [count] with empty PK/sort (singleton-only, §8);
grouped ⇒ the group columns are the PK and sort, then the aggregate columns.
output: Cell<Option<OutEdge>>The single downstream edge, wired two-phase like every fan-out-seam operator
(via Graph::set_output).
Implementations§
Source§impl Reduce
impl Reduce
Sourcepub fn global_agg(input: NodeId, storage: StorageId, spec: AggSpec) -> Reduce
pub fn global_agg(input: NodeId, storage: StorageId, spec: AggSpec) -> Reduce
A global single-aggregate reducer over input (spec). Output is a
one-column [<agg>] row (no key, no sort — a singleton, §8).
Sourcepub fn grouped_agg(
input: NodeId,
storage: StorageId,
partition_key: Vec<ColId>,
key_cols: Vec<&str>,
spec: AggSpec,
) -> Reduce
pub fn grouped_agg( input: NodeId, storage: StorageId, partition_key: Vec<ColId>, key_cols: Vec<&str>, spec: AggSpec, ) -> Reduce
A grouped single-aggregate reducer: one [group_key…, <agg>] row per distinct
value of partition_key (input-row column indices). key_cols are the output
names of the group columns (same arity/order as partition_key). The output
schema’s PK and sort are the group columns (0..k, ascending), then the aggregate.
Sourcepub fn count(input: NodeId, storage: StorageId) -> Reduce
pub fn count(input: NodeId, storage: StorageId) -> Reduce
A global count(*) reducer over input — global_agg
with AggSpec::Count.
Sourcepub fn sum(input: NodeId, storage: StorageId, col: ColId) -> Reduce
pub fn sum(input: NodeId, storage: StorageId, col: ColId) -> Reduce
A global sum(col) reducer (input-row column index).
Sourcepub fn avg(input: NodeId, storage: StorageId, col: ColId) -> Reduce
pub fn avg(input: NodeId, storage: StorageId, col: ColId) -> Reduce
A global avg(col) reducer (input-row column index).
Sourcepub fn count_by(
input: NodeId,
storage: StorageId,
partition_key: Vec<ColId>,
key_cols: Vec<&str>,
) -> Reduce
pub fn count_by( input: NodeId, storage: StorageId, partition_key: Vec<ColId>, key_cols: Vec<&str>, ) -> Reduce
A grouped count(*) reducer — grouped_agg with
AggSpec::Count.
Sourcepub fn sum_by(
input: NodeId,
storage: StorageId,
partition_key: Vec<ColId>,
key_cols: Vec<&str>,
col: ColId,
) -> Reduce
pub fn sum_by( input: NodeId, storage: StorageId, partition_key: Vec<ColId>, key_cols: Vec<&str>, col: ColId, ) -> Reduce
A grouped sum(col) reducer (col in input-row coordinates).
Sourcepub fn avg_by(
input: NodeId,
storage: StorageId,
partition_key: Vec<ColId>,
key_cols: Vec<&str>,
col: ColId,
) -> Reduce
pub fn avg_by( input: NodeId, storage: StorageId, partition_key: Vec<ColId>, key_cols: Vec<&str>, col: ColId, ) -> Reduce
A grouped avg(col) reducer (col in input-row coordinates).
Sourcepub fn lazy(self) -> Reduce
pub fn lazy(self) -> Reduce
Switch a grouped reducer to the lazy regime (REDUCE-DESIGN.md §8.1) — for
a relationship aggregate whose groups a parent join fetches one at a time. A
constrained fetch folds just that group (persisting even a count-0 group); a
push to a group with no state is dropped (the next fetch folds it), and a
group that drains to 0 keeps its slot so the next Add re-births it.
Sourcepub fn with_input_types(self, input: &Schema) -> Reduce
pub fn with_input_types(self, input: &Schema) -> Reduce
Derive the synthetic output schema’s column_types from the input
schema (design 226 §4.1): each group column preserves its input column’s
declared type (partition_key[i] in input coordinates → output position
i), and each aggregate column carries AggSpec::output_type. The
builder chains this on every reduce it constructs; the constructors alone
leave Schema::new’s all-Number default, which direct-op tests keep.
Sourcepub fn evict_partition(&self, g: &Graph, constraint: &Constraint)
pub fn evict_partition(&self, g: &Graph, constraint: &Constraint)
Delete the group slot constraint identifies — the twin of
Take::evict_partition, called by
Graph::evict_child_partitions when a parent leaves a bounded parent view.
A lazy grouped reducer (every relationship aggregate) never deletes a slot on
its own: the grouped-delta death arm deletes only when eager, and it
must stay that way — the lazy regime keeps a count-0 slot precisely so a later
Add can re-birth 0 → 1 for a parent that is still in the view. Nothing else
dropped a lazy slot, so a long-lived query kept one entry per parent row it ever
aggregated, for the life of the query. The parent’s departure is the event that
makes the slot dead, which is what this hook is.
Safe for the same reason Take::evict_partition is: both callers gate on the
parent correlation being the parent’s primary key, so the group belongs to exactly
the one parent that left, and a deleted lazy slot is indistinguishable from a
never-folded one — the next constrained fetch re-folds it.
The constraint is in output coordinates (the join’s child key for an aggregate
is 0..k, builder.rs), while Reduce::partition_key is in input coordinates —
so the gate and the value extraction both work in 0..k, exactly as
Reduce::fetch’s single-group path does. Matching against partition_key
directly would silently evict nothing whenever the correlation is not the child’s
leading columns.
Sourcepub fn fetch<'g>(
&'g self,
g: &'g Graph,
req: &FetchRequest,
) -> Box<dyn Iterator<Item = Node<'g>> + 'g>
pub fn fetch<'g>( &'g self, g: &'g Graph, req: &FetchRequest, ) -> Box<dyn Iterator<Item = Node<'g>> + 'g>
Lazy pull. The request is ignored for the eager regimes (see the module note):
global serves/folds the single accumulator; grouped folds the whole input into
per-group accumulators, persists each, and emits one row per group in output-sort
order. On first sight the fold seeds state; thereafter push maintains it.
Sourcepub fn push<'g>(&'g self, g: &'g Graph, change: Change<'g>)
pub fn push<'g>(&'g self, g: &'g Graph, change: Change<'g>)
Eager push. Global maintains the one immortal accumulator (always an Edit);
grouped maintains per-group accumulators with birth (Add), shift (Edit), and
death (Remove at row count → 0). A same-group Edit leaves count(*)
unchanged but can still move a sum/avg, so it re-folds the accumulator and
emits iff the aggregate row actually changed (REDUCE-DESIGN.md §6); a Child
never changes the input-row population, so it emits nothing.