@omgbase/oqx 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +42 -2
- package/dist/ast.d.ts +35 -1
- package/dist/ast.d.ts.map +1 -1
- package/dist/ast.js +7 -1
- package/dist/ast.js.map +1 -1
- package/dist/build.d.ts +7 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +9 -2
- package/dist/build.js.map +1 -1
- package/dist/engine.d.ts +2 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +136 -9
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/optimize/analysis.d.ts.map +1 -1
- package/dist/optimize/analysis.js +15 -1
- package/dist/optimize/analysis.js.map +1 -1
- package/dist/optimize/ir.d.ts +5 -1
- package/dist/optimize/ir.d.ts.map +1 -1
- package/dist/optimize/ir.js +2 -0
- package/dist/optimize/ir.js.map +1 -1
- package/dist/optimize/rules.d.ts.map +1 -1
- package/dist/optimize/rules.js +4 -3
- package/dist/optimize/rules.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +402 -20
- package/dist/parser.js.map +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +16 -0
- package/dist/print.js.map +1 -1
- package/dist/references.d.ts +6 -4
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +65 -3
- package/dist/references.js.map +1 -1
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +20 -1
- package/dist/resolve.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/walk.d.ts +1 -1
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +10 -0
- package/dist/walk.js.map +1 -1
- package/package.json +1 -1
- package/src/ast.ts +44 -2
- package/src/build.ts +11 -4
- package/src/engine.ts +136 -9
- package/src/index.ts +1 -1
- package/src/optimize/analysis.ts +12 -1
- package/src/optimize/ir.ts +7 -1
- package/src/optimize/rules.ts +4 -3
- package/src/parser.ts +324 -20
- package/src/print.ts +18 -3
- package/src/references.ts +62 -7
- package/src/resolve.ts +22 -2
- package/src/version.ts +1 -1
- package/src/walk.ts +12 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to `@omgbase/oqx` are recorded here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
|
5
5
|
[Semantic Versioning](https://semver.org/) (pre-1.0: a minor bump may break).
|
|
6
6
|
|
|
7
|
+
## [0.24.0] - 2026-10-15
|
|
8
|
+
|
|
9
|
+
### Minor
|
|
10
|
+
- **OQX 0.24 — `group by` with aggregates** (`spec/oqx` 0.23 → 0.24; new suite `group-by` (94 cases): 1379 → 1473 fixtures; `projection::projection may not use count` moved to the positive case). The clause order gains `group by <key>, …` and `having <pred>` after `follow` and before `order by` (`select from where frontier follow group by having order by limit offset`; a destination block adds `depth`). **Grouping** (SEMANTICS §24): the rows a body yields become one row per distinct key tuple — key values compare as identities (absent ≡ `null`, a host row by its id, else structurally), groups come in first-appearance order; a key is `name: expr` or an unaliased navigation named by its last segment (an expression key needs the alias); a **group scope** sits where the rows did, its row the key record (one property per key, the first row's value), so a bare key name reads the key, `$it` is the record, `$key` its structural identity, and **`rows`** is the group's rows — a value and a receiver (`rows collect { select title }`, `rows count { where checked }`, `rows first { … }`, `having rows exists { … }`) inside whose block `^` reads the keys. Any other bare name at the group scope is the parse error `` `title` is not a group key or aggregate — … a key (`group by title`), an aggregate over its rows (`collect(title)`), or the rows themselves (`rows collect { select title }`) `` (plus `` `median(…)` is not an aggregate `` inside an unknown free call). **Aggregates** — reserved free-function names `count sum min max avg collect first last any all`, parsing to a new `aggregate` node wherever written (never a `call`; a host cannot serve a function by these names), legal only in a select item, `having` or `order by` of a grouped body (elsewhere a parse error naming the clause, judged over the alias-resolved `where`/`select`; nesting is an error): `count()` counts rows; every value aggregate sees the **present** values of its argument — `count(e)`, `sum(e)` (`0` of none; a non-number is the eval error `cannot sum …`), `avg(e)` (absent of none), `min(e)`/`max(e)` (§6 ordering, an unorderable value leaves the extreme unchanged, absent of none), `collect(e)` (the list in group order), `first(e)`/`last(e)` — with the SQL-spelled modifier `count(distinct e)` / `collect(distinct e)` (identity dedup; `count(distinct)` reads a field named `distinct`; rejected on `any`/`all`); `any(p)`/`all(p)` test truthiness (absent is false) and short-circuit. **`having`** filters the groups and is alias-resolved like `where` (`having n > 1`); `order by` reads the group scope (`order by count() desc`; an alias there is the unknown-name error); `limit`/`offset` bound the groups; every consumer reduces the groups (`docs count { group by layer }`); a `where`-position test may group (`where jobs exists { group by employer having count() > 1 }`); `distinct` with `group by` is the parse error `` `distinct` is redundant with `group by` ``; a grouped body with no `select` projects its keys and `count: count()` (synthesized by the parser; a key named `count` then needs a `select`); `follow` + `group by` groups the occurrences. **`count { … }` is a value** wherever a value stands (`n: jobs count { }`, `jobs count { } > 2`); `exists`/`none` stay predicates. **AST** (AST.md): `query.groupBy`/`having` and `subquery.groupBy`/`having` (materialized `null`), `groupBy { keys }` and `key { name, expr }` nodes, `aggregate { name, arg, distinct }`; `CHILDREN` visits `groupBy`/`having` after `follow` (clauses `groupBy`, `having`); `print` writes every key aliased (`group by city: city`) in clause order and an aggregate as its call; `references` (§5b) reports key expressions in clause `groupBy` on the rows, an aggregate's argument on the rows, and a name read at the group scope with a `groupBy` step (`"group by city: city"`) — a bare key name there, `rows`, and `^key` from a nested block are not references; `build.groupBy`/`key`/`aggregate`; `isAggregateName`/`AGGREGATE_NAMES` exported. Engine: group scopes (`Scope.group`), `rows` resolution, aggregates over the group's row scopes (lifts and walk intrinsics readable); the optimizer never answers a grouped block from cardinality and treats a grouped block as may-raise; `optimized ≡ naive` holds over every fixture. `LANGUAGE_VERSION` 0.24.
|
|
11
|
+
- `@omgbase/oqx-syntax`: `LANGUAGE_VERSION` 0.24; `group by` and `having` highlighted as clauses, the aggregates (`AGGREGATES`) as builtin functions before `(`, `rows` (`GROUP_ROWS`) as an intrinsic; `group`/`having` join the contextual words; assets regenerated; parity over every fixture.
|
|
12
|
+
|
|
7
13
|
## [0.23.0] - 2026-10-13
|
|
8
14
|
|
|
9
15
|
### Minor
|
package/README.md
CHANGED
|
@@ -58,6 +58,8 @@ once, and every one is optional except `from` at the top level:
|
|
|
58
58
|
from <collection> from ${people}
|
|
59
59
|
[ where <predicate> ] where age >= 18 and jobs exists { where not end }
|
|
60
60
|
[ follow <dest>, … ] follow children { depth 4 } (or: follow before, after; follow ^people { where … })
|
|
61
|
+
[ group by <key>, … ] group by city, decade: (age - age % 10) / 10 (0.24; then select reads keys, aggregates, rows)
|
|
62
|
+
[ having <predicate> ] having count() > 1
|
|
61
63
|
[ order by <expr> … ] order by age desc, name
|
|
62
64
|
[ limit N ] limit 10
|
|
63
65
|
[ offset N ] offset 20
|
|
@@ -72,8 +74,8 @@ projects and `jobs { is current }` filters — see §10). At the top level a
|
|
|
72
74
|
predicate always needs `where`, and `from ${people} count` is an error rather
|
|
73
75
|
than a projection of a field called `count`. Writing a clause out of
|
|
74
76
|
order is a parse error that names the order (`` `select` must come before
|
|
75
|
-
`from` — OQX clause order is select, from, where, follow,
|
|
76
|
-
offset ``). The same body grammar applies inside every consumer block
|
|
77
|
+
`from` — OQX clause order is select, from, where, frontier, follow, group by,
|
|
78
|
+
having, order by, limit, offset ``). The same body grammar applies inside every consumer block
|
|
77
79
|
(`collect { … }`, `exists { … }`, …), where `from` is optional because the
|
|
78
80
|
receiver supplies the rows.
|
|
79
81
|
|
|
@@ -584,6 +586,44 @@ oqx`name from ${people} where jobs exists { offset 1 }`; // ha
|
|
|
584
586
|
A storage adapter that pushes the whole query may translate them to SQL
|
|
585
587
|
`LIMIT`/`OFFSET`; the shipped `SqliteTable` leaves them to the residual.
|
|
586
588
|
|
|
589
|
+
### 8c. Grouping and aggregates (0.24)
|
|
590
|
+
|
|
591
|
+
`group by <key>, …` — after `where`/`follow`, before `order by` — turns the rows
|
|
592
|
+
a body yields into **one row per distinct key tuple**. A key is a bare name
|
|
593
|
+
(`city`), a dotted navigation named by its last segment (`meta.region`), or an
|
|
594
|
+
aliased expression (`decade: (age - age % 10) / 10`); key values compare as
|
|
595
|
+
identities (absent ≡ `null`), groups come in first-appearance order. After the
|
|
596
|
+
grouping the body reads the **group**: a select item, `having` and `order by`
|
|
597
|
+
may name a key, an **aggregate** over the group's rows, or `rows` — the group's
|
|
598
|
+
rows, a value and a receiver (`rows collect { name values }`, `rows count { where
|
|
599
|
+
active }`, `rows first { … }`); any other bare name is a parse error naming it.
|
|
600
|
+
Without a `select` the projection is the keys plus `count: count()`.
|
|
601
|
+
|
|
602
|
+
```js
|
|
603
|
+
oqx`from ${people} group by city`;
|
|
604
|
+
// [{ city: "NYC", count: 2 }, { city: "SF", count: 1 }]
|
|
605
|
+
oqx`select city, n: count(), oldest: max(age), names: collect(name) from ${people} group by city order by count() desc`;
|
|
606
|
+
oqx`select city, active: rows count { where active } from ${people} group by city having count() > 1`;
|
|
607
|
+
oqx`select decade, n: count() from ${people} group by decade: (age - age % 10) / 10 order by decade`;
|
|
608
|
+
oqx`${people} count { group by city }`; // the number of groups
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
The aggregates are reserved free-function names, legal only in a grouped body's
|
|
612
|
+
select, `having` or `order by`: `count()` counts the group's rows; every other
|
|
613
|
+
aggregate sees the **present** values of its argument — `count(e)`, `sum(e)`
|
|
614
|
+
(`0` of none; a non-number is an eval error), `avg(e)`, `min(e)`, `max(e)`
|
|
615
|
+
(OQX ordering; absent of none), `collect(e)` (the list, in group order),
|
|
616
|
+
`first(e)`, `last(e)` — with `distinct` as a modifier (`count(distinct e)`,
|
|
617
|
+
`collect(distinct e)`); `any(p)` / `all(p)` test truthiness over the rows.
|
|
618
|
+
Aggregates do not nest. `having` is the post-group `where` (it may name a select
|
|
619
|
+
alias: `having n > 1`); `order by` reads the group scope and is not rewritten
|
|
620
|
+
against aliases (`order by count()`, not `order by n`); `limit`/`offset` bound
|
|
621
|
+
the groups; `distinct` with `group by` is an error (redundant). `count { … }` is
|
|
622
|
+
now a value wherever a value stands (`n: jobs count { where pay > 1 }`), in or
|
|
623
|
+
out of a group. A nested block may group too (`jobs collect { employer, total:
|
|
624
|
+
sum(pay) group by employer }`), and every consumer reduces the groups (`jobs
|
|
625
|
+
count { group by employer }`). Spec: `spec/oqx/SEMANTICS.md` §24.
|
|
626
|
+
|
|
587
627
|
### 9. Recursion: `follow`
|
|
588
628
|
|
|
589
629
|
`follow <destination>, …` turns a query into a bounded recursive traversal: the
|
package/dist/ast.d.ts
CHANGED
|
@@ -26,6 +26,12 @@ export declare function isRegexOp(op: string): op is RegexOp;
|
|
|
26
26
|
export type UnaryOp = "!" | "-";
|
|
27
27
|
/** The operator of a `logical` expression. */
|
|
28
28
|
export type LogicalOp = "&&" | "||";
|
|
29
|
+
/** The aggregate functions (SEMANTICS §24, since 0.24): reserved free-function
|
|
30
|
+
* names that reduce a group's rows. `count` takes zero or one argument, every
|
|
31
|
+
* other exactly one; `any`/`all` take a predicate and admit no `distinct`. */
|
|
32
|
+
export type AggregateName = "count" | "sum" | "min" | "max" | "avg" | "collect" | "first" | "last" | "any" | "all";
|
|
33
|
+
export declare const AGGREGATE_NAMES: ReadonlySet<string>;
|
|
34
|
+
export declare function isAggregateName(name: string): name is AggregateName;
|
|
29
35
|
/** One part of a template literal (since 0.21): a decoded text run, or an
|
|
30
36
|
* interpolated expression. The strings are not nodes (no `kind`, no span). */
|
|
31
37
|
export type TemplatePart = string | Expr;
|
|
@@ -98,7 +104,27 @@ export type Expr = {
|
|
|
98
104
|
kind: "template";
|
|
99
105
|
span: Span;
|
|
100
106
|
parts: TemplatePart[];
|
|
107
|
+
} | {
|
|
108
|
+
kind: "aggregate";
|
|
109
|
+
span: Span;
|
|
110
|
+
name: AggregateName;
|
|
111
|
+
arg: Expr | null;
|
|
112
|
+
distinct: boolean;
|
|
101
113
|
} | OpNode;
|
|
114
|
+
/** One `group by` key (since 0.24): `name: expr`, or an unaliased navigation
|
|
115
|
+
* keyed by its last segment, exactly as a `select` item is. */
|
|
116
|
+
export interface GroupKey {
|
|
117
|
+
kind: "key";
|
|
118
|
+
span: Span;
|
|
119
|
+
name: string;
|
|
120
|
+
expr: Expr;
|
|
121
|
+
}
|
|
122
|
+
/** The `group by` clause (since 0.24): one row per distinct key tuple. */
|
|
123
|
+
export interface GroupBy {
|
|
124
|
+
kind: "groupBy";
|
|
125
|
+
span: Span;
|
|
126
|
+
keys: GroupKey[];
|
|
127
|
+
}
|
|
102
128
|
/** One `order by` term. */
|
|
103
129
|
export interface OrderSpec {
|
|
104
130
|
kind: "order";
|
|
@@ -172,6 +198,11 @@ export interface Subquery {
|
|
|
172
198
|
* `depth` is destination-only. Both `null` when absent. */
|
|
173
199
|
frontier: Expr | null;
|
|
174
200
|
depth: number | null;
|
|
201
|
+
/** `group by` / `having` (since 0.24): the block's rows become one row per
|
|
202
|
+
* distinct key tuple (SEMANTICS §24); `having` filters the groups. Both
|
|
203
|
+
* `null` when absent; `having` is set only together with `groupBy`. */
|
|
204
|
+
groupBy: GroupBy | null;
|
|
205
|
+
having: Where | null;
|
|
175
206
|
/** `values` — scalar projection mode: the (single) projected expression is the
|
|
176
207
|
* row's result itself rather than being wrapped in a `{ name: value }` record,
|
|
177
208
|
* so `name values` yields `["Bob", …]` and `$it values` yields the rows. */
|
|
@@ -220,6 +251,9 @@ export interface Query {
|
|
|
220
251
|
* scope, like `where`) is a frontier (`$stop == "frontier"`, not expanded).
|
|
221
252
|
* `null` without one. */
|
|
222
253
|
frontier: Expr | null;
|
|
254
|
+
/** `group by` / `having` (since 0.24; see Subquery). */
|
|
255
|
+
groupBy: GroupBy | null;
|
|
256
|
+
having: Where | null;
|
|
223
257
|
/** `distinct` — dedup the result rows by their projected value (see OpNode). */
|
|
224
258
|
distinct: boolean;
|
|
225
259
|
/** `values` — scalar projection mode (see Subquery). */
|
|
@@ -229,7 +263,7 @@ export interface Query {
|
|
|
229
263
|
offset: Expr | null;
|
|
230
264
|
}
|
|
231
265
|
/** Every node of the tree. `AstNode["kind"]` is the closed set of node kinds. */
|
|
232
|
-
export type AstNode = Query | Subquery | OpNode | Follow | OrderSpec | SelectItem | Where | Expr;
|
|
266
|
+
export type AstNode = Query | Subquery | OpNode | Follow | GroupBy | GroupKey | OrderSpec | SelectItem | Where | Expr;
|
|
233
267
|
/** The closed set of node kinds. */
|
|
234
268
|
export type AstKind = AstNode["kind"];
|
|
235
269
|
/** Whether a node is a scalar `Expr` (an `op` in expression position is not: it
|
package/dist/ast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
|
package/dist/ast.js
CHANGED
|
@@ -20,9 +20,15 @@ export const REGEX_OPS = {
|
|
|
20
20
|
export function isRegexOp(op) {
|
|
21
21
|
return Object.hasOwn(REGEX_OPS, op);
|
|
22
22
|
}
|
|
23
|
+
export const AGGREGATE_NAMES = new Set([
|
|
24
|
+
"count", "sum", "min", "max", "avg", "collect", "first", "last", "any", "all",
|
|
25
|
+
]);
|
|
26
|
+
export function isAggregateName(name) {
|
|
27
|
+
return AGGREGATE_NAMES.has(name);
|
|
28
|
+
}
|
|
23
29
|
/** The expression kinds (`Expr["kind"]`), for `isExpr`. */
|
|
24
30
|
const EXPR_KINDS = new Set([
|
|
25
|
-
"lit", "ident", "outer", "binding", "member", "call", "unary", "required", "binary", "logical", "in", "range", "tuple", "template",
|
|
31
|
+
"lit", "ident", "outer", "binding", "member", "call", "unary", "required", "binary", "logical", "in", "range", "tuple", "template", "aggregate",
|
|
26
32
|
]);
|
|
27
33
|
/** Whether a node is a scalar `Expr` (an `op` in expression position is not: it
|
|
28
34
|
* is an expression only by position, see AST.md §1). */
|
package/dist/ast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,+EAA+E;AAC/E,2EAA2E;AAC3E,gFAAgF;AAChF,wDAAwD;AACxD,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,4EAA4E;AAC5E,wCAAwC;AA0BxC,oEAAoE;AACpE,MAAM,CAAC,MAAM,SAAS,GAA+E;IACnG,GAAG,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE;IACzC,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE;IACpC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IACzC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE;CACrC,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;
|
|
1
|
+
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,+EAA+E;AAC/E,2EAA2E;AAC3E,gFAAgF;AAChF,wDAAwD;AACxD,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,4EAA4E;AAC5E,wCAAwC;AA0BxC,oEAAoE;AACpE,MAAM,CAAC,MAAM,SAAS,GAA+E;IACnG,GAAG,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE;IACzC,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE;IACpC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IACzC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE;CACrC,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAaD,MAAM,CAAC,MAAM,eAAe,GAAwB,IAAI,GAAG,CAAgB;IACzE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;CAC9E,CAAC,CAAC;AAEH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAqMD,2DAA2D;AAC3D,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC;IAC9C,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW;CAChJ,CAAC,CAAC;AAEH;wDACwD;AACxD,MAAM,UAAU,MAAM,CAAC,IAAa;IAClC,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
|
package/dist/build.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BinaryOp, Consumer, CountCmp, Expr, Follow, FollowDestination, LogicalOp, OpNode, OrderSpec, Query, SelectItem, Span, Subquery, TemplatePart, UnaryOp, Where } from "./ast.js";
|
|
1
|
+
import type { AggregateName, BinaryOp, Consumer, CountCmp, Expr, Follow, FollowDestination, GroupBy, GroupKey, LogicalOp, OpNode, OrderSpec, Query, SelectItem, Span, Subquery, TemplatePart, UnaryOp, Where } from "./ast.js";
|
|
2
2
|
/** A fresh empty span (never shared: spans are mutable tuples). */
|
|
3
3
|
export declare const span: () => Span;
|
|
4
4
|
export declare const lit: (value: string | number | boolean | null) => Expr;
|
|
@@ -21,6 +21,8 @@ export declare const tuple: (items: Expr[]) => Expr;
|
|
|
21
21
|
/** `` `text ${expr} …` `` — a template literal (SEMANTICS §7b); `parts` alternates
|
|
22
22
|
* text, expression, text, … (odd length, text first and last). */
|
|
23
23
|
export declare const template: (parts: TemplatePart[]) => Expr;
|
|
24
|
+
/** `name(arg)` / `name(distinct arg)` / `count()` — an aggregate over the group's rows (SEMANTICS §24). */
|
|
25
|
+
export declare const aggregate: (name: AggregateName, arg?: Expr | null, distinct?: boolean) => Expr;
|
|
24
26
|
export declare const scalar: (expr: Expr) => Where;
|
|
25
27
|
export declare const and: (parts: Where[]) => Where;
|
|
26
28
|
export declare const or: (parts: Where[]) => Where;
|
|
@@ -28,6 +30,10 @@ export declare const not: (expr: Where) => Where;
|
|
|
28
30
|
export declare const field: (name: string, expr: Expr, lift?: number) => SelectItem;
|
|
29
31
|
export declare const collect: (name: string, op: OpNode) => SelectItem;
|
|
30
32
|
export declare const order: (expr: Expr, desc?: boolean) => OrderSpec;
|
|
33
|
+
/** One `group by` key, `name: expr`. */
|
|
34
|
+
export declare const key: (name: string, expr: Expr) => GroupKey;
|
|
35
|
+
/** `group by <keys>` (SEMANTICS §24). */
|
|
36
|
+
export declare const groupBy: (keys: GroupKey[]) => GroupBy;
|
|
31
37
|
/** A block body; every clause defaults to empty/absent. */
|
|
32
38
|
export declare function subquery(parts?: Partial<Omit<Subquery, "kind" | "span">>): Subquery;
|
|
33
39
|
/** `<receiver> <op> [distinct] { sub } [<relop> N]`. */
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
package/dist/build.js
CHANGED
|
@@ -24,6 +24,8 @@ export const tuple = (items) => ({ kind: "tuple", span: span(), items });
|
|
|
24
24
|
/** `` `text ${expr} …` `` — a template literal (SEMANTICS §7b); `parts` alternates
|
|
25
25
|
* text, expression, text, … (odd length, text first and last). */
|
|
26
26
|
export const template = (parts) => ({ kind: "template", span: span(), parts });
|
|
27
|
+
/** `name(arg)` / `name(distinct arg)` / `count()` — an aggregate over the group's rows (SEMANTICS §24). */
|
|
28
|
+
export const aggregate = (name, arg = null, distinct = false) => ({ kind: "aggregate", span: span(), name, arg, distinct });
|
|
27
29
|
export const scalar = (expr) => ({ kind: "scalar", span: span(), expr });
|
|
28
30
|
export const and = (parts) => ({ kind: "and", span: span(), parts });
|
|
29
31
|
export const or = (parts) => ({ kind: "or", span: span(), parts });
|
|
@@ -31,11 +33,16 @@ export const not = (expr) => ({ kind: "not", span: span(), expr });
|
|
|
31
33
|
export const field = (name, expr, lift = 0) => ({ kind: "field", span: span(), name, expr, lift });
|
|
32
34
|
export const collect = (name, op) => ({ kind: "collect", span: span(), name, op });
|
|
33
35
|
export const order = (expr, desc = false) => ({ kind: "order", span: span(), expr, desc });
|
|
36
|
+
/** One `group by` key, `name: expr`. */
|
|
37
|
+
export const key = (name, expr) => ({ kind: "key", span: span(), name, expr });
|
|
38
|
+
/** `group by <keys>` (SEMANTICS §24). */
|
|
39
|
+
export const groupBy = (keys) => ({ kind: "groupBy", span: span(), keys });
|
|
34
40
|
/** A block body; every clause defaults to empty/absent. */
|
|
35
41
|
export function subquery(parts = {}) {
|
|
36
42
|
return {
|
|
37
43
|
kind: "subquery", span: span(),
|
|
38
|
-
from: [], where: null, select: [], orderBy: null, follow: null, frontier: null, depth: null,
|
|
44
|
+
from: [], where: null, select: [], orderBy: null, follow: null, frontier: null, depth: null, groupBy: null, having: null,
|
|
45
|
+
values: false, limit: null, offset: null,
|
|
39
46
|
...parts,
|
|
40
47
|
};
|
|
41
48
|
}
|
|
@@ -53,7 +60,7 @@ export function follow(destinations, opts = {}) {
|
|
|
53
60
|
export function query(source, parts = {}) {
|
|
54
61
|
return {
|
|
55
62
|
kind: "query", span: span(), source,
|
|
56
|
-
from: [], where: null, select: [], orderBy: null, consumer: "collect", follow: null, frontier: null,
|
|
63
|
+
from: [], where: null, select: [], orderBy: null, consumer: "collect", follow: null, frontier: null, groupBy: null, having: null,
|
|
57
64
|
distinct: false, values: false, limit: null, offset: null,
|
|
58
65
|
...parts,
|
|
59
66
|
};
|
package/dist/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+EAA+E;AAC/E,+EAA+E;AAC/E,mEAAmE;AAOnE,mEAAmE;AACnE,MAAM,CAAC,MAAM,IAAI,GAAG,GAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEvC,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,KAAuC,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;AAC7G,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACrF,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAc,EAAE,IAAY,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7G,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAa,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3F,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAU,EAAE,IAAY,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AACzG,kCAAkC;AAClC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,GAAG,IAAc,EAAQ,EAAE,CAAC,IAAI,CAAC,MAAM,CAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AACxH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,IAAiB,EAAE,IAAY,EAAE,OAAe,EAAE,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AACrI,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAW,EAAE,IAAU,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACpG,oDAAoD;AACpD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAU,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACzF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EAAY,EAAE,IAAU,EAAE,KAAW,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3H,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,EAAa,EAAE,IAAU,EAAE,KAAW,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC9H,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,IAAU,EAAE,KAAW,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACnG,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAe,EAAE,EAAe,EAAE,YAAY,GAAG,KAAK,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;AAC/I,0EAA0E;AAC1E,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;AACvF;kEACkE;AAClE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAqB,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts
CHANGED
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACV,KAAK,EAAe,MAAM,EAC3B,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMhD,OAAO,KAAK,EAA0B,IAAI,EAAY,MAAM,qBAAqB,CAAC;AAIlF;;;kDAGkD;AAClD,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACzD;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACnC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,GAC1C;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;CAC5D;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -26,6 +26,7 @@ import { printTemplate } from "./print.js";
|
|
|
26
26
|
import { DEFAULT_RULES, HashIndex, intersectPositions, lookupOrder, planFor, residualWithout } from "./optimize/index.js";
|
|
27
27
|
const RECUR = new Set(["$depth", "$stop", "$leaf", "$frontier", "$ordinal"]);
|
|
28
28
|
const KEY = "$key";
|
|
29
|
+
const ROWS = "rows";
|
|
29
30
|
const HARD_DEPTH_CAP = 8;
|
|
30
31
|
// The `$key: expr` item of a projection, if any — the identity the rows it
|
|
31
32
|
// yields carry (SEMANTICS §16, since 0.20).
|
|
@@ -65,10 +66,11 @@ export class InMemoryEngine {
|
|
|
65
66
|
if (query.follow)
|
|
66
67
|
return this.runFollow(query, rows, root, bound);
|
|
67
68
|
// Consumer-directed short-circuits (skipped under `distinct`, which must
|
|
68
|
-
// materialize + dedup by projection before reducing
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
69
|
+
// materialize + dedup by projection before reducing, and under `group by`,
|
|
70
|
+
// whose consumer counts groups). Counting never materializes rows;
|
|
71
|
+
// exists/none stop as soon as the bound is known to be non-empty (the
|
|
72
|
+
// (offset+1)th match — or the first, when unbounded).
|
|
73
|
+
if (!query.distinct && !query.groupBy && (query.consumer === "exists" || query.consumer === "none" || query.consumer === "count")) {
|
|
72
74
|
const need = query.consumer === "count" ? Infinity : bound.offset + 1;
|
|
73
75
|
let n = 0;
|
|
74
76
|
for (const r of rows) {
|
|
@@ -88,7 +90,7 @@ export class InMemoryEngine {
|
|
|
88
90
|
// first over an unordered, non-distinct set needs only the rows up to the
|
|
89
91
|
// bound (offset + 1). single materializes everything so its error can
|
|
90
92
|
// report how many rows actually matched.
|
|
91
|
-
const cap = query.consumer === "first" && !query.orderBy && !query.distinct
|
|
93
|
+
const cap = query.consumer === "first" && !query.orderBy && !query.distinct && !query.groupBy
|
|
92
94
|
? bound.offset + Math.min(1, bound.limit ?? 1)
|
|
93
95
|
: Infinity;
|
|
94
96
|
let kept = [];
|
|
@@ -100,6 +102,8 @@ export class InMemoryEngine {
|
|
|
100
102
|
break;
|
|
101
103
|
}
|
|
102
104
|
}
|
|
105
|
+
if (query.groupBy)
|
|
106
|
+
kept = this.groupScopes(kept, query, root);
|
|
103
107
|
this.applyKey(kept, query);
|
|
104
108
|
this.sortScopes(kept, query.orderBy);
|
|
105
109
|
if (query.distinct)
|
|
@@ -207,11 +211,108 @@ export class InMemoryEngine {
|
|
|
207
211
|
if (extra.meta)
|
|
208
212
|
s.meta = extra.meta;
|
|
209
213
|
if (isEntry(row)) {
|
|
214
|
+
s.raw = row;
|
|
210
215
|
s.row = row.value;
|
|
211
216
|
s.meta = { ...(s.meta ?? {}), [KEY]: row.key };
|
|
212
217
|
}
|
|
213
218
|
return s;
|
|
214
219
|
}
|
|
220
|
+
// ---- grouping (SEMANTICS §24) ----------------------------------------------
|
|
221
|
+
// The kept scopes of a body, one GROUP scope per distinct key tuple, in order
|
|
222
|
+
// of first appearance; then `having` over each group. A group scope sits where
|
|
223
|
+
// the rows did (same parent, same depth): its row is the key record — one
|
|
224
|
+
// property per key, in key order, holding the first row's value — so bare
|
|
225
|
+
// names read the keys, `$it` is the record and `$key` its structural identity;
|
|
226
|
+
// `rows` yields the group's rows and the aggregates reduce their scopes. Key
|
|
227
|
+
// values compare as identities (§16): absent ≡ null, a host row by its id,
|
|
228
|
+
// anything else structurally.
|
|
229
|
+
groupScopes(scopes, body, parent) {
|
|
230
|
+
const g = body.groupBy;
|
|
231
|
+
const groups = new Map();
|
|
232
|
+
for (const s of scopes) {
|
|
233
|
+
const record = {};
|
|
234
|
+
const ids = [];
|
|
235
|
+
for (const k of g.keys) {
|
|
236
|
+
const v = this.evalExpr(k.expr, s);
|
|
237
|
+
record[k.name] = v;
|
|
238
|
+
ids.push(canonicalKey(this.hostIdentity(v)));
|
|
239
|
+
}
|
|
240
|
+
const id = ids.join("");
|
|
241
|
+
let gs = groups.get(id);
|
|
242
|
+
if (!gs) {
|
|
243
|
+
gs = { row: record, parent, bindings: parent.bindings, run: parent.run, lifts: {}, group: { rowScopes: [], rows: [] } };
|
|
244
|
+
groups.set(id, gs);
|
|
245
|
+
}
|
|
246
|
+
gs.group.rowScopes.push(s);
|
|
247
|
+
gs.group.rows.push(s.raw !== undefined ? s.raw : s.row);
|
|
248
|
+
}
|
|
249
|
+
const out = [...groups.values()];
|
|
250
|
+
return body.having ? out.filter((gs) => this.evalWhere(body.having, gs)) : out;
|
|
251
|
+
}
|
|
252
|
+
// An aggregate (§24) over the group of the scope it is read in. The argument
|
|
253
|
+
// is read on each row's scope in group order; every value aggregate sees the
|
|
254
|
+
// PRESENT values only (`count()` alone counts rows), `distinct` dedups them by
|
|
255
|
+
// identity (§16), and `any`/`all` test truthiness (absent is false) and stop
|
|
256
|
+
// at the first row that decides, like `||` / `&&`.
|
|
257
|
+
aggregate(e, scope) {
|
|
258
|
+
const g = scope.group;
|
|
259
|
+
if (!g)
|
|
260
|
+
throw new OqxError(`\`${describe(e)}\` is an aggregate and is read on a group — it needs a \`group by\` in its body`, "eval");
|
|
261
|
+
if (e.arg === null)
|
|
262
|
+
return g.rowScopes.length;
|
|
263
|
+
const arg = e.arg;
|
|
264
|
+
if (e.name === "any")
|
|
265
|
+
return g.rowScopes.some((s) => truthy(this.evalExpr(arg, s)));
|
|
266
|
+
if (e.name === "all")
|
|
267
|
+
return g.rowScopes.every((s) => truthy(this.evalExpr(arg, s)));
|
|
268
|
+
let values = [];
|
|
269
|
+
for (const s of g.rowScopes) {
|
|
270
|
+
const v = this.evalExpr(arg, s);
|
|
271
|
+
if (v != null)
|
|
272
|
+
values.push(v);
|
|
273
|
+
}
|
|
274
|
+
if (e.distinct) {
|
|
275
|
+
const seen = new Set();
|
|
276
|
+
values = values.filter((v) => {
|
|
277
|
+
const k = canonicalKey(this.hostIdentity(v));
|
|
278
|
+
if (seen.has(k))
|
|
279
|
+
return false;
|
|
280
|
+
seen.add(k);
|
|
281
|
+
return true;
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
switch (e.name) {
|
|
285
|
+
case "count": return values.length;
|
|
286
|
+
case "collect": return values;
|
|
287
|
+
case "first": return values.length > 0 ? values[0] : undefined;
|
|
288
|
+
case "last": return values.length > 0 ? values[values.length - 1] : undefined;
|
|
289
|
+
case "sum":
|
|
290
|
+
case "avg": {
|
|
291
|
+
let total = 0;
|
|
292
|
+
for (const v of values) {
|
|
293
|
+
if (typeof v !== "number") {
|
|
294
|
+
throw new OqxError(`cannot ${e.name === "sum" ? "sum" : "average"} \`${describe(arg)}\` over the group: it is ${describeValueKind(v)} — \`${e.name}\` takes numbers`, "eval");
|
|
295
|
+
}
|
|
296
|
+
total += v;
|
|
297
|
+
}
|
|
298
|
+
if (e.name === "sum")
|
|
299
|
+
return total;
|
|
300
|
+
return values.length === 0 ? undefined : total / values.length;
|
|
301
|
+
}
|
|
302
|
+
case "min":
|
|
303
|
+
case "max": {
|
|
304
|
+
if (values.length === 0)
|
|
305
|
+
return undefined;
|
|
306
|
+
let best = values[0];
|
|
307
|
+
for (let i = 1; i < values.length; i++) {
|
|
308
|
+
const c = compare(values[i], best);
|
|
309
|
+
if (c !== undefined && (e.name === "min" ? c < 0 : c > 0))
|
|
310
|
+
best = values[i];
|
|
311
|
+
}
|
|
312
|
+
return best;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
215
316
|
// ---- follow ---------------------------------------------------------------
|
|
216
317
|
runFollow(query, rows, root, bound) {
|
|
217
318
|
const { seed, post } = query.where ? partitionRecur(query.where) : { seed: null, post: null };
|
|
@@ -220,6 +321,8 @@ export class InMemoryEngine {
|
|
|
220
321
|
let scopes = occ.map((o) => this.enter(o.row, root, { lifts: {}, meta: o.meta }));
|
|
221
322
|
if (post)
|
|
222
323
|
scopes = scopes.filter((s) => this.evalWhere(post, s));
|
|
324
|
+
if (query.groupBy)
|
|
325
|
+
scopes = this.groupScopes(scopes, query, root);
|
|
223
326
|
this.sortScopes(scopes, query.orderBy);
|
|
224
327
|
if (query.distinct)
|
|
225
328
|
scopes = this.dedupByProjection(scopes, query);
|
|
@@ -452,9 +555,10 @@ export class InMemoryEngine {
|
|
|
452
555
|
n = boundedCount(acc.rows.length, bound);
|
|
453
556
|
scope.run.trace?.({ kind: "cardinality", count: n });
|
|
454
557
|
}
|
|
455
|
-
else if (op.op !== "count" && bound === UNBOUNDED) {
|
|
558
|
+
else if (op.op !== "count" && bound === UNBOUNDED && !op.sub.groupBy) {
|
|
456
559
|
// Unbounded exists/none: stop at the first match (dedup cannot change
|
|
457
|
-
// emptiness). Bounded
|
|
560
|
+
// emptiness). Bounded, or grouped (`having` may empty the groups): the
|
|
561
|
+
// offset/limit decide emptiness, so materialize.
|
|
458
562
|
n = this.anyMatch(op, scope, acc) ? 1 : 0;
|
|
459
563
|
}
|
|
460
564
|
else
|
|
@@ -483,9 +587,11 @@ export class InMemoryEngine {
|
|
|
483
587
|
}
|
|
484
588
|
return this.opRows(op, scope, bound).length > 0;
|
|
485
589
|
}
|
|
486
|
-
// The rows a consumer op reduces: matched → ordered → distinct → bounded.
|
|
590
|
+
// The rows a consumer op reduces: matched → grouped → ordered → distinct → bounded.
|
|
487
591
|
opRows(op, scope, bound, acc) {
|
|
488
592
|
let scopes = this.matchRows(op, scope, acc);
|
|
593
|
+
if (op.sub.groupBy)
|
|
594
|
+
scopes = this.groupScopes(scopes, op.sub, scope);
|
|
489
595
|
this.applyKey(scopes, op.sub);
|
|
490
596
|
this.sortScopes(scopes, op.sub.orderBy);
|
|
491
597
|
if (op.distinct)
|
|
@@ -692,6 +798,8 @@ export class InMemoryEngine {
|
|
|
692
798
|
// Over a single receiver (one object) a `collect` is the record or absent,
|
|
693
799
|
// not a one-element list (SEMANTICS §3).
|
|
694
800
|
case "collect": return single ? (scopes.length > 0 ? this.projectRow(op.sub, scopes[0]) : undefined) : scopes.map((s) => this.projectRow(op.sub, s));
|
|
801
|
+
// `count { … }` as a value (0.24): the number of rows the block yields.
|
|
802
|
+
case "count": return scopes.length;
|
|
695
803
|
default: return scopes.length > 0 ? this.projectRow(op.sub, scopes[0]) : null;
|
|
696
804
|
}
|
|
697
805
|
}
|
|
@@ -711,6 +819,8 @@ export class InMemoryEngine {
|
|
|
711
819
|
const { rows, where } = this.access(op, plan, scope);
|
|
712
820
|
const seeds = where ? rows.filter((r) => this.evalWhere(where, this.enter(r, scope, { lifts: {} }))) : rows;
|
|
713
821
|
scopes = this.followWalk(this.keyed(seeds, sub, scope), sub.follow, destination ? null : sub.frontier, scope).map((o) => this.enter(o.row, scope, { meta: o.meta }));
|
|
822
|
+
if (sub.groupBy)
|
|
823
|
+
scopes = this.groupScopes(scopes, sub, scope);
|
|
714
824
|
this.sortScopes(scopes, sub.orderBy);
|
|
715
825
|
if (op.distinct)
|
|
716
826
|
scopes = this.dedupByProjection(scopes, sub);
|
|
@@ -722,7 +832,8 @@ export class InMemoryEngine {
|
|
|
722
832
|
scopes = this.opRows(op, scope, bound, acc);
|
|
723
833
|
}
|
|
724
834
|
switch (op.op) {
|
|
725
|
-
case "collect":
|
|
835
|
+
case "collect":
|
|
836
|
+
case "count": return { scopes, single };
|
|
726
837
|
case "first": return { scopes: scopes.slice(0, 1), single };
|
|
727
838
|
case "single":
|
|
728
839
|
if (scopes.length > 1)
|
|
@@ -881,6 +992,8 @@ export class InMemoryEngine {
|
|
|
881
992
|
}
|
|
882
993
|
return absent ? undefined : out;
|
|
883
994
|
}
|
|
995
|
+
// An aggregate (SEMANTICS §24) over the group of the current scope.
|
|
996
|
+
case "aggregate": return this.aggregate(e, scope);
|
|
884
997
|
}
|
|
885
998
|
}
|
|
886
999
|
// Resolve a name against ONE scope — never its ancestors. A scope provides,
|
|
@@ -913,6 +1026,9 @@ export class InMemoryEngine {
|
|
|
913
1026
|
// `$key` is the row's identity on every row (SEMANTICS §16, since 0.20).
|
|
914
1027
|
if (name === KEY)
|
|
915
1028
|
return this.scopeIdentity(scope);
|
|
1029
|
+
// `rows` on a group scope is the group's rows (§24); elsewhere a property.
|
|
1030
|
+
if (name === ROWS && scope.group)
|
|
1031
|
+
return scope.group.rows;
|
|
916
1032
|
if (RECUR.has(name) && scope.meta && Object.hasOwn(scope.meta, name))
|
|
917
1033
|
return scope.meta[name];
|
|
918
1034
|
if (scope.lifts && Object.hasOwn(scope.lifts, name))
|
|
@@ -1026,6 +1142,16 @@ function describe(e) {
|
|
|
1026
1142
|
const t = printTemplate(e);
|
|
1027
1143
|
return t.strings.map((s, i) => (i < t.count ? `${s}\${${t.indices[i]}}` : s)).join("");
|
|
1028
1144
|
}
|
|
1145
|
+
// The kind of a value, for an aggregate's error: `a string ("abc")`, `a list`, …
|
|
1146
|
+
function describeValueKind(v) {
|
|
1147
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean")
|
|
1148
|
+
return `a ${typeof v} (${JSON.stringify(v)})`;
|
|
1149
|
+
if (isRange(v))
|
|
1150
|
+
return "a range";
|
|
1151
|
+
if (Array.isArray(v) || (typeof v === "object" && v !== null && !isEntry(v) && Symbol.iterator in v))
|
|
1152
|
+
return "a list";
|
|
1153
|
+
return "a record";
|
|
1154
|
+
}
|
|
1029
1155
|
function describeReceiver(e) {
|
|
1030
1156
|
if (e.kind === "ident")
|
|
1031
1157
|
return e.name;
|
|
@@ -1071,6 +1197,7 @@ function exprHasRecur(e) {
|
|
|
1071
1197
|
case "range": return (e.lo != null && exprHasRecur(e.lo)) || (e.hi != null && exprHasRecur(e.hi));
|
|
1072
1198
|
case "tuple": return e.items.some(exprHasRecur);
|
|
1073
1199
|
case "template": return e.parts.some((p) => typeof p !== "string" && exprHasRecur(p));
|
|
1200
|
+
case "aggregate": return e.arg !== null && exprHasRecur(e.arg);
|
|
1074
1201
|
default: return false;
|
|
1075
1202
|
}
|
|
1076
1203
|
}
|