Skip to content

Aggregation builders ​

5.x only

This page applies to the 5.x line only (npm 5.1.x, branch 5.x). From 6.0, @ahoo-wang/fetcher-wow lives in the Wow repository (typescript/) and is documented at wow.ahoo.me; its successor @ahoo-wang/wow-client is on npm from Wow 9.2.0 (9.2.1 or later accepts fetcher 6).

AggregationQuery describes a server aggregation, not a JavaScript reducer. Supply at least one metric. aggregate(query, attributes?, controller?) returns flat rows keyed by your aliases; aggregateStream returns JSON SSE rows and needs explicit stream consumption. Generics describe rows but do not validate their contents.

BuilderInputs / result
aggregation.element(path, predicate?)Select an array/nested element path, with optional element-relative filter; root metadata/search/deletion filters are rejected inside predicate.
field(field), constant(number)Field reference or finite numeric constant expression.
add/subtract/multiply/divide(left, right)Binary expression tree; division is evaluated by the backend, not here.
terms(field, alias, missingKey?)Terms grouping.
histogram(field, {interval, alias})Finite interval > 0.
dateHistogram(field, {unit, alias, timeZone?, dense?})AggregationDateUnit from YEAR to SECOND, timeZone defaults UTC; empty zone and invalid enum throw.
count(alias, predicate?)Count metric; no field argument.
any(field, alias, predicate?)Backend-selected value; do not treat it as a deterministic first row.
sum/avg/min/max/stddev/variance(expression, alias, predicate?)Numeric metric over an expression.

Query fields are filter, elements, groupBy, metrics (nonempty tuple), sort, limit, having. Root filter selects source documents; elements describes nested element traversal/filtering, and group/metric fields refer to that aggregation scope. Output stays flat; elements does not mean a nested output response. Sort fields refer to output aliases. Omitted filter/groupBy/sort/limit are left undefined; there is no hidden client limit or grouping.

Field syntax is validated by the same logical-field validator as filter. Aliases must be one valid segment, cannot contain dots or begin __wow. Invalid aliases, nonfinite constants and invalid histogram options throw TypeError. These checks do not guarantee fields exist, are numeric, or that a backend supports the query; service errors still reject. Builders perform no I/O or cleanup. Streaming readers must be cancelled and released when abandoned.

Complete example ​

ts
import { aggregation, filter, desc } from '@ahoo-wang/fetcher-wow';
import type { AggregationQuery } from '@ahoo-wang/fetcher-wow';
export const revenue: AggregationQuery = {
  filter: filter.eq('state.status', 'PAID'),
  elements: [aggregation.element('state.lines', filter.gt('quantity', 0))],
  groupBy: [aggregation.terms('sku', 'sku')],
  metrics: [
    aggregation.sum(
      aggregation.multiply(
        aggregation.field('price'),
        aggregation.field('quantity'),
      ),
      'revenue',
    ),
    aggregation.count('rows'),
  ],
  sort: [desc('revenue')],
  limit: 20,
};

Extended aggregation protocol ​

Aligned with Wow main at fd1b3cd46. Existing builder calls keep their JSON shape; optional metric filters, missingKey, and dense are omitted unless supplied.

  • aggregation.distinctCount(expression, alias, predicate?) counts distinct non-null contributions; aggregation.percentile(expression, percentile, alias, predicate?) accepts finite values strictly between 0 and 100 (use 50 for the median). stddev and variance compute population statistics. Percentiles are approximate on both backends; Elasticsearch distinct counts may be approximate, while MongoDB counts distinct values exactly.
  • Non-derived metrics accept an optional FilterExpression in the current aggregation scope. It affects only that metric. The backend validates scalar fields and rejects unsupported filter operators.
  • aggregation.derived(expression, alias) uses a DerivedExpression tree (METRIC_REF, CONSTANT, BINARY). References must name earlier metrics and cannot reference ANY. Derived metrics have no record filter; filter the referenced metrics instead. Null operands or division by zero produce null.
  • having?: HavingExpression supports CONDITION, BETWEEN, IN, IS_NULL, AND, and OR, using metric aliases, not field paths. ComparisonOperator contains EQ, NE, GT, GTE, LT, LTE. HAVING requires grouping and cannot reference ANY; it runs before sorting and limit. Non-empty sets/operands are enforced by tuple types; finite values, bounds, references and depth are validated by the server.
  • terms(field, alias, missingKey?) optionally merges missing/null values into a nonblank string bucket key; the backend validates string field support. dateHistogram(field, { unit, alias, timeZone?, dense? }) fills interior date gaps when dense is true; it requires the only group dimension. No rows means no generated date range.

Backend requirements still apply (MongoDB 5.1+ for dense groups, 7.0+ for percentiles); the client performs no backend capability probing. Raw typed expression objects are not runtime validators.

ts
import {
  aggregation,
  AggregationExpressionOperator,
  ComparisonOperator,
  DerivedExpressionType,
  HavingExpressionType,
  type AggregationQuery,
} from '@ahoo-wang/fetcher-wow';

const query: AggregationQuery = {
  groupBy: [aggregation.terms('state.status', 'status', 'Unknown')],
  metrics: [
    aggregation.count('orders'),
    aggregation.sum(aggregation.field('state.amount'), 'revenue'),
    aggregation.derived(
      {
        type: DerivedExpressionType.BINARY,
        operator: AggregationExpressionOperator.DIVIDE,
        left: { type: DerivedExpressionType.METRIC_REF, metric: 'revenue' },
        right: { type: DerivedExpressionType.METRIC_REF, metric: 'orders' },
      },
      'averageOrderValue',
    ),
  ],
  having: {
    type: HavingExpressionType.CONDITION,
    metric: 'averageOrderValue',
    operator: ComparisonOperator.GTE,
    value: 100,
  },
};

Public signatures and types ​

These signatures follow declarations reachable from the current root entry. ? marks optional input; generics/interfaces only constrain compile-time types. Locate inherited and related types through the symbol index. Runtime defaults and failure behavior are described above.

AggregationGroupType ​

ts
export enum AggregationGroupType {
  TERMS = 'TERMS',
  HISTOGRAM = 'HISTOGRAM',
  DATE_HISTOGRAM = 'DATE_HISTOGRAM',
}

packages/wow/src/query/aggregation.ts:22

AggregationMetricType ​

ts
export enum AggregationMetricType {
  COUNT = 'COUNT',
  NUMERIC = 'NUMERIC',
  ANY = 'ANY',
  DISTINCT_COUNT = 'DISTINCT_COUNT',
  PERCENTILE = 'PERCENTILE',
  DERIVED = 'DERIVED',
}

packages/wow/src/query/aggregation.ts:28

AggregationExpressionType ​

ts
export enum AggregationExpressionType {
  FIELD = 'FIELD',
  CONSTANT = 'CONSTANT',
  BINARY = 'BINARY',
}

packages/wow/src/query/aggregation.ts:37

AggregationExpressionOperator ​

ts
export enum AggregationExpressionOperator {
  ADD = 'ADD',
  SUBTRACT = 'SUBTRACT',
  MULTIPLY = 'MULTIPLY',
  DIVIDE = 'DIVIDE',
}

packages/wow/src/query/aggregation.ts:43

AggregationDateUnit ​

ts
export enum AggregationDateUnit {
  YEAR = 'YEAR',
  QUARTER = 'QUARTER',
  MONTH = 'MONTH',
  WEEK = 'WEEK',
  DAY = 'DAY',
  HOUR = 'HOUR',
  MINUTE = 'MINUTE',
  SECOND = 'SECOND',
}

packages/wow/src/query/aggregation.ts:50

AggregationFunction ​

ts
export enum AggregationFunction {
  SUM = 'SUM',
  AVG = 'AVG',
  MIN = 'MIN',
  MAX = 'MAX',
  STDDEV = 'STDDEV',
  VARIANCE = 'VARIANCE',
}

packages/wow/src/query/aggregation.ts:61

AggregationElement ​

ts
export interface AggregationElement {
  path: QueryField;
  filter?: ElementFilterExpression;
}

packages/wow/src/query/aggregation.ts:70

TermsAggregationGroup ​

ts
export interface TermsAggregationGroup<
  FIELDS extends string = string,
> extends AggregationGroupBase<FIELDS> {
  type: AggregationGroupType.TERMS;
  missingKey?: string;
}

packages/wow/src/query/aggregation.ts:80

HistogramAggregationGroup ​

ts
export interface HistogramAggregationGroup<
  FIELDS extends string = string,
> extends AggregationGroupBase<FIELDS> {
  type: AggregationGroupType.HISTOGRAM;
  interval: number;
}

packages/wow/src/query/aggregation.ts:87

DateHistogramAggregationGroup ​

ts
export interface DateHistogramAggregationGroup<
  FIELDS extends string = string,
> extends AggregationGroupBase<FIELDS> {
  type: AggregationGroupType.DATE_HISTOGRAM;
  unit: AggregationDateUnit;
  timeZone?: string;
  dense?: boolean;
}

packages/wow/src/query/aggregation.ts:94

AggregationGroup ​

ts
export type AggregationGroup<FIELDS extends string = string> =
  | TermsAggregationGroup<FIELDS>
  | HistogramAggregationGroup<FIELDS>
  | DateHistogramAggregationGroup<FIELDS>;

packages/wow/src/query/aggregation.ts:103

FieldAggregationExpression ​

ts
export interface FieldAggregationExpression<FIELDS extends string = string> {
  type: AggregationExpressionType.FIELD;
  field: QueryField<FIELDS>;
}

packages/wow/src/query/aggregation.ts:108

ConstantAggregationExpression ​

ts
export interface ConstantAggregationExpression {
  type: AggregationExpressionType.CONSTANT;
  value: number;
}

packages/wow/src/query/aggregation.ts:113

BinaryAggregationExpression ​

ts
export interface BinaryAggregationExpression<FIELDS extends string = string> {
  type: AggregationExpressionType.BINARY;
  operator: AggregationExpressionOperator;
  left: AggregationExpression<FIELDS>;
  right: AggregationExpression<FIELDS>;
}

packages/wow/src/query/aggregation.ts:118

AggregationExpression ​

ts
export type AggregationExpression<FIELDS extends string = string> =
  | FieldAggregationExpression<FIELDS>
  | ConstantAggregationExpression
  | BinaryAggregationExpression<FIELDS>;

packages/wow/src/query/aggregation.ts:125

CountAggregationMetric ​

ts
export interface CountAggregationMetric<FIELDS extends string = string> {
  filter?: FilterExpression<FIELDS>;
  type: AggregationMetricType.COUNT;
  alias: string;
}

packages/wow/src/query/aggregation.ts:130

NumericAggregationMetric ​

ts
export interface NumericAggregationMetric<FIELDS extends string = string> {
  filter?: FilterExpression<FIELDS>;
  type: AggregationMetricType.NUMERIC;
  function: AggregationFunction;
  expression: AggregationExpression<FIELDS>;
  alias: string;
}

packages/wow/src/query/aggregation.ts:136

AnyAggregationMetric ​

ts
export interface AnyAggregationMetric<FIELDS extends string = string> {
  filter?: FilterExpression<FIELDS>;
  type: AggregationMetricType.ANY;
  field: QueryField<FIELDS>;
  alias: string;
}

packages/wow/src/query/aggregation.ts:144

DistinctCountAggregationMetric ​

ts
export interface DistinctCountAggregationMetric<
  FIELDS extends string = string,
> {
  type: AggregationMetricType.DISTINCT_COUNT;
  expression: AggregationExpression<FIELDS>;
  alias: string;
  filter?: FilterExpression<FIELDS>;
}

packages/wow/src/query/aggregation.ts:151

PercentileAggregationMetric ​

ts
export interface PercentileAggregationMetric<FIELDS extends string = string> {
  type: AggregationMetricType.PERCENTILE;
  expression: AggregationExpression<FIELDS>;
  percentile: number;
  alias: string;
  filter?: FilterExpression<FIELDS>;
}

packages/wow/src/query/aggregation.ts:160

DerivedExpressionType ​

ts
export enum DerivedExpressionType {
  METRIC_REF = 'METRIC_REF',
  CONSTANT = 'CONSTANT',
  BINARY = 'BINARY',
}

packages/wow/src/query/aggregation.ts:168

DerivedExpression ​

ts
export type DerivedExpression =
  | { type: DerivedExpressionType.METRIC_REF; metric: string }
  | { type: DerivedExpressionType.CONSTANT; value: number }
  | {
      type: DerivedExpressionType.BINARY;
      operator: AggregationExpressionOperator;
      left: DerivedExpression;
      right: DerivedExpression;
    };

packages/wow/src/query/aggregation.ts:175

DerivedAggregationMetric ​

ts
export interface DerivedAggregationMetric {
  type: AggregationMetricType.DERIVED;
  expression: DerivedExpression;
  alias: string;
}

packages/wow/src/query/aggregation.ts:185

HavingExpressionType ​

ts
export enum HavingExpressionType {
  CONDITION = 'CONDITION',
  BETWEEN = 'BETWEEN',
  IN = 'IN',
  IS_NULL = 'IS_NULL',
  AND = 'AND',
  OR = 'OR',
}

packages/wow/src/query/aggregation.ts:191

ComparisonOperator ​

ts
export enum ComparisonOperator {
  EQ = 'EQ',
  NE = 'NE',
  GT = 'GT',
  GTE = 'GTE',
  LT = 'LT',
  LTE = 'LTE',
}

packages/wow/src/query/aggregation.ts:200

HavingExpression ​

ts
export type HavingExpression =
  | {
      type: HavingExpressionType.CONDITION;
      metric: string;
      operator: ComparisonOperator;
      value: number;
    }
  | {
      type: HavingExpressionType.BETWEEN;
      metric: string;
      lower: number;
      upper: number;
    }
  | {
      type: HavingExpressionType.IN;
      metric: string;
      values: [number, ...number[]];
    }
  | { type: HavingExpressionType.IS_NULL; metric: string; negated?: boolean }
  | {
      type: HavingExpressionType.AND | HavingExpressionType.OR;
      operands: [HavingExpression, ...HavingExpression[]];
    };

packages/wow/src/query/aggregation.ts:210

AggregationMetric ​

ts
export type AggregationMetric<FIELDS extends string = string> =
  | CountAggregationMetric<FIELDS>
  | NumericAggregationMetric<FIELDS>
  | AnyAggregationMetric<FIELDS>
  | DistinctCountAggregationMetric<FIELDS>
  | PercentileAggregationMetric<FIELDS>
  | DerivedAggregationMetric;

packages/wow/src/query/aggregation.ts:234

AggregationQuery ​

ts
export interface AggregationQuery<
  ROOT_FIELDS extends string = string,
  AGGREGATION_FIELDS extends string = ROOT_FIELDS,
> {
  filter?: FilterExpression<ROOT_FIELDS>;
  elements?: AggregationElement[];
  groupBy?: AggregationGroup<AGGREGATION_FIELDS>[];
  metrics: [
    AggregationMetric<AGGREGATION_FIELDS>,
    ...AggregationMetric<AGGREGATION_FIELDS>[],
  ];
  sort?: FieldSort[];
  limit?: number;
  having?: HavingExpression;
}

packages/wow/src/query/aggregation.ts:242

HistogramAggregationOptions ​

ts
export interface HistogramAggregationOptions {
  interval: number;
  alias: string;
}

packages/wow/src/query/aggregation.ts:258

DateHistogramAggregationOptions ​

ts
export interface DateHistogramAggregationOptions {
  unit: AggregationDateUnit;
  alias: string;
  timeZone?: string;
  dense?: boolean;
}

packages/wow/src/query/aggregation.ts:263

aggregation ​

ts
export declare const aggregation: {
  element(
    path: string,
    predicate?: ElementFilterExpression,
  ): AggregationElement;
  field<FIELDS extends string>(
    field: FIELDS,
  ): FieldAggregationExpression<FIELDS>;
  constant(value: number): ConstantAggregationExpression;
  add: <FIELDS extends string>(
    left: AggregationExpression<FIELDS>,
    right: AggregationExpression<FIELDS>,
  ) => BinaryAggregationExpression<FIELDS>;
  subtract: <FIELDS extends string>(
    left: AggregationExpression<FIELDS>,
    right: AggregationExpression<FIELDS>,
  ) => BinaryAggregationExpression<FIELDS>;
  multiply: <FIELDS extends string>(
    left: AggregationExpression<FIELDS>,
    right: AggregationExpression<FIELDS>,
  ) => BinaryAggregationExpression<FIELDS>;
  divide: <FIELDS extends string>(
    left: AggregationExpression<FIELDS>,
    right: AggregationExpression<FIELDS>,
  ) => BinaryAggregationExpression<FIELDS>;
  terms<FIELDS extends string>(
    field: FIELDS,
    alias: string,
    missingKey?: string,
  ): TermsAggregationGroup<FIELDS>;
  histogram<FIELDS extends string>(
    field: FIELDS,
    { interval, alias }: HistogramAggregationOptions,
  ): HistogramAggregationGroup<FIELDS>;
  dateHistogram<FIELDS extends string>(
    field: FIELDS,
    { unit, alias, timeZone, dense }: DateHistogramAggregationOptions,
  ): DateHistogramAggregationGroup<FIELDS>;
  any<FIELDS extends string>(
    field: FIELDS,
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ): AnyAggregationMetric<FIELDS>;
  count<FIELDS extends string = string>(
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ): CountAggregationMetric<FIELDS>;
  sum: <FIELDS extends string>(
    expression: AggregationExpression<FIELDS>,
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ) => NumericAggregationMetric<FIELDS>;
  avg: <FIELDS extends string>(
    expression: AggregationExpression<FIELDS>,
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ) => NumericAggregationMetric<FIELDS>;
  min: <FIELDS extends string>(
    expression: AggregationExpression<FIELDS>,
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ) => NumericAggregationMetric<FIELDS>;
  max: <FIELDS extends string>(
    expression: AggregationExpression<FIELDS>,
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ) => NumericAggregationMetric<FIELDS>;
  stddev: <FIELDS extends string>(
    expression: AggregationExpression<FIELDS>,
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ) => NumericAggregationMetric<FIELDS>;
  variance: <FIELDS extends string>(
    expression: AggregationExpression<FIELDS>,
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ) => NumericAggregationMetric<FIELDS>;
  distinctCount<FIELDS extends string>(
    expression: AggregationExpression<FIELDS>,
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ): DistinctCountAggregationMetric<FIELDS>;
  percentile<FIELDS extends string>(
    expression: AggregationExpression<FIELDS>,
    percentile: number,
    alias: string,
    predicate?: FilterExpression<FIELDS>,
  ): PercentileAggregationMetric<FIELDS>;
  derived(
    expression: DerivedExpression,
    alias: string,
  ): DerivedAggregationMetric;
};

packages/wow/src/query/aggregation.ts:310

Released under the Apache License 2.0.