@omgbase/oqx 0.18.0 → 0.20.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 +24 -0
- package/README.md +149 -64
- package/dist/ast.d.ts +21 -5
- package/dist/ast.d.ts.map +1 -1
- package/dist/ast.js +1 -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 +7 -3
- package/dist/build.js.map +1 -1
- package/dist/context.d.ts +19 -5
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +6 -9
- package/dist/context.js.map +1 -1
- package/dist/engine.d.ts +12 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +235 -83
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/optimize/analysis.d.ts +3 -2
- package/dist/optimize/analysis.d.ts.map +1 -1
- package/dist/optimize/analysis.js +14 -15
- package/dist/optimize/analysis.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +196 -114
- package/dist/parser.js.map +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +13 -13
- package/dist/print.js.map +1 -1
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +11 -8
- 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 +5 -3
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +26 -8
- package/dist/walk.js.map +1 -1
- package/package.json +1 -1
- package/src/ast.ts +23 -6
- package/src/build.ts +8 -7
- package/src/context.ts +22 -11
- package/src/engine.ts +236 -90
- package/src/index.ts +2 -1
- package/src/optimize/analysis.ts +13 -12
- package/src/parser.ts +180 -96
- package/src/print.ts +12 -10
- package/src/resolve.ts +11 -7
- package/src/version.ts +1 -1
- package/src/walk.ts +32 -11
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ 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.20.0] - 2026-10-11
|
|
8
|
+
|
|
9
|
+
### Minor
|
|
10
|
+
- OQX 0.20 (`spec/oqx` 0.19 → 0.20; 1188 → 1258 fixtures, three new suites `tuples`, `identity`, `cardinality`):
|
|
11
|
+
|
|
12
|
+
- **`follow` loses its options block.** A brace after *any* destination opens that destination's block (`follow children { depth 2 }`, `follow a, children { id != "root" }`, `follow entries(kids) { where … }`), with the 0.17 body rules. Two clauses belong to a destination block alone, after `where` in the fixed order: `depth <1..8>` (this destination expands a frontier row only while its `$depth < n`) and `frontier <expr>` (an occurrence reached through this destination for which it holds is a frontier; read with `^` the walk's enclosing scope; seeds are never frontiers). The former `where` option is the block's `where`; `by` is gone. When several destinations reach a row in one step the first governs the occurrence. **The body that carries the `follow` may carry `frontier <expr>` too** (Brendan, 2026-10-11): it applies to the SEEDS — a seed for which it holds (read in the seed's own scope, `^` the enclosing scope, like `where`) is `$stop == "frontier"` and is not expanded, while it stays in the result — so every block that feeds rows into the walk says which of its rows are frontiers, and the frontier relevant to the current `$it` is defined separately from each destination's. The clause order of every body is `select from where frontier follow order by limit offset` (a destination block's adds `depth` after `frontier`); a body-level `frontier` without a `follow` is the parse error `` `frontier` needs a `follow` ``, a body-level `depth` an error naming the destination block; inside a destination block `frontier` is always the destination's. AST: `follow` keeps only `destinations`/`distinct`; `subquery` gains `frontier`/`depth`, `query` gains `frontier`; `print` writes `follow x collect { where … depth 2 }` and a body's `frontier` between `where` and `follow`.
|
|
13
|
+
- **Tuples.** `(e1, e2, …)` with two or more elements is a value and a receiver: the elements concatenated with one level of flattening (a collection contributes its items, a row itself; an absent element nothing, a `null` is kept; duplicates kept). `size((a, b))`, `"x" in (tags, aliases)`, `(mom, dad, siblings) { name values }`, `follow (before, after) { depth 2 }` (one destination over the union). New AST node `tuple { items }`.
|
|
14
|
+
- **Identity is `$key`.** One meaning — the key a row is known by — for `follow` cycle detection, the per-step union, `follow distinct`, `distinct` over unprojected rows, `is`, and reading `$key` on any row. The default is the host's identity through a new `DataContext` seam (TS: optional `identityOf(row)`, replacing the required `identity`, which stays as a deprecated alias for one minor; Rust: `identity` now has a structural default), else the row's structural value; an entry's identity is its key. The language never names an `id` property: the "`id` if present" rule is removed, and the in-memory `DefaultContext` names no identity. A block may project `$key: <expr>` to give the rows it yields that identity (`select name, $key: email from people follow next { $key: email }`); in a destination block `$key:` is not a column. Caveat: identities compare as values across the whole walk, so destinations that can reach the same rows should project the same `$key`.
|
|
15
|
+
- **Result shape by receiver cardinality.** In row position an array is a collection and a non-array object is one row: a `collect` over it — `doc { title }`, `title from doc`, a nested `d: dst { title }` — yields the one record (or absent when filtered out; `doc { status == "active" }` is a conditional reach-through), not a one-element list; `values` the one value; `first`/`single` that object. A scalar in row position is an eval error (`` `s` is a string ("abc"), not a collection or an object ``) instead of a one-row collection — `list(s)` says it explicitly. A `from` re-projection or a `follow` makes the result plural. Hosts say which of their objects are collections through the new optional `DataContext.isSingle` (TS; default: a non-iterable object is one row) / `is_single` (Rust; default `true`). TS `OqxResult` for `collect` gains `single: boolean` (`unwrap` yields the record or `null`); Rust gains `OqxResult::Record(Option<Value>)`.
|
|
16
|
+
- **Sugar closures.** `x { … }` ≡ `x collect { … }` wherever a value is accepted, function arguments included (`size(children { is checked })`); `x distinct { … }` ≡ `x collect distinct { … }`.
|
|
17
|
+
|
|
18
|
+
omgbase binding (surface stays 3.0; the dialect follows the language):
|
|
19
|
+
|
|
20
|
+
- `DataContext.identityOf` (TS) / `identity` (Rust) answers a store row's id for every kind (`doc_id`, `block_id`, `node_id`, `edge_id`), so `$key` on any hit is its id and walks dedup by it as before; the binding no longer relies on the removed `id`-property rule.
|
|
21
|
+
- The statically single relations `doc`, `block` and `dst` hand out an absent-single marker when they have no row, so `dst { title }` on a link row is one record and, on an external or dangling link, absent — not `[]`; as a value the marker is absent (`dst == null`, `has(dst)`, `dst.title` unchanged). The Rust context's `is_single` marks the lazy root scans as collections.
|
|
22
|
+
- The `graph` tool compiles to `follow distinct <dir>_links collect { from <dst|doc> depth <n> }`; `query_syntax`, the `query` tool description, both help cards, `docs/query-language.md`, `docs/graph-and-query.md`, `docs/mcp-api.md` and the OQX tutorial describe the 0.20 forms (body-level `frontier` included); the planners treat a destination block's `frontier`/`depth` as its own, never push a tuple down, and a body with `frontier` plans like one with a `follow` — in memory (pinned on both sides). `spec/surface` `query-follow::body-frontier-keeps-seeds-unexpanded` walks the processes with the unverified one kept but not expanded.
|
|
23
|
+
- `spec/surface` fixtures using the old options block are rewritten to the destination-block forms (`… collect { from dst depth 2 }`; `by type` → `{ $key: type from dst }`) and regenerated; `spec/cli` and `interop.json` regenerated with them.
|
|
24
|
+
- `@omgbase/oqx-syntax`: `LANGUAGE_VERSION` 0.20; `FOLLOW_OPTIONS` → `DESTINATION_CLAUSES` (`frontier`, `depth`; `by` is no longer a word of the language), assets regenerated.
|
|
25
|
+
|
|
26
|
+
## [0.19.0] - 2026-10-10
|
|
27
|
+
|
|
28
|
+
### Minor
|
|
29
|
+
- OQX language 0.19 — outer-reference `follow` destinations take the implicit `collect`. A `follow` destination whose receiver is headed by an outer reference (`^docs`, `^^root.rel`, `0^docs`, `^$it.docs`, with any member chain) can never be a plain destination (plain destinations are relations of the current row), so a `{` right after it now opens a **destination block** with the implicit `collect`: `follow refs(before), ^docs { where ^$path in list(after) }` walks `before` references forward and, backward, every document whose `after` names the frontier row — exactly `^docs collect { … }` with the 0.17 body rules (where-first, projections). Until 0.18 that query failed with the outer-reference error, because the 0.17 carve-out reserved a brace after the last destination for the options block. Any other receiver (a bare relation, a call like `refs(before)` or `entries(kids)`, a binding) keeps that reading: a brace after it is the options block when it is the last destination (`follow children { depth 2 }`, `follow a, children { depth 2 }`) and the "a destination block needs its consumer" error when the brace holds a predicate. An options block may still follow a trailing destination block (`follow ^docs { where … } { depth 2 }`). A plain `follow ^children` (no brace, no consumer) is still an error; its message now names both block spellings: `` `follow` takes a relation of the current row (`follow <relation>`); an outer reference walks a root collection only as a destination block — `follow ^docs { where … }` or `follow ^docs collect { … }` ``. Fixtures: 25 new cases across `follow`, `sugar`, `errors-parse` and `ast` (1188 total); the 0.17 sugar case "an outer-reference destination with a brace is the outer-reference error" is replaced by its positive form. `LANGUAGE_VERSION` is 0.19 in both engines and in `@omgbase/oqx-syntax`; the `query_syntax` reference (`@omgbase/core`, `omgbase-surface`) describes the new rule.
|
|
30
|
+
|
|
7
31
|
## [0.18.0] - 2026-10-10
|
|
8
32
|
|
|
9
33
|
### Minor
|
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ const company = "Globocorp";
|
|
|
34
34
|
const employees = oqx`
|
|
35
35
|
name, id, title
|
|
36
36
|
from ${people}
|
|
37
|
-
where jobs exists { where employer == ${company}
|
|
37
|
+
where jobs exists { where employer == ${company} and not end_date }
|
|
38
38
|
`;
|
|
39
39
|
// → [{ name: "Bob", id: 124, title: "Engineer" }, …] (current Globocorp employees)
|
|
40
40
|
```
|
|
@@ -56,8 +56,8 @@ once, and every one is optional except `from` at the top level:
|
|
|
56
56
|
```
|
|
57
57
|
[ [select] projection ] name, id, title: label (or: select distinct …, … values)
|
|
58
58
|
from <collection> from ${people}
|
|
59
|
-
[ where <predicate> ] where age >= 18
|
|
60
|
-
[ follow <dest>, … ] follow children { depth 4 } (or: follow before, after; follow ^people
|
|
59
|
+
[ where <predicate> ] where age >= 18 and jobs exists { where not end }
|
|
60
|
+
[ follow <dest>, … ] follow children { depth 4 } (or: follow before, after; follow ^people { where … })
|
|
61
61
|
[ order by <expr> … ] order by age desc, name
|
|
62
62
|
[ limit N ] limit 10
|
|
63
63
|
[ offset N ] offset 20
|
|
@@ -200,19 +200,22 @@ const flags = { beta: { on: true }, legacy: { on: false } };
|
|
|
200
200
|
oqx`$key values from entries(${flags}) where on`; // ["beta"] (bare `on` reads the value)
|
|
201
201
|
|
|
202
202
|
oqx`name, on: entries(prefs) collect { $key values where $it } from ${users}`; // as a nested receiver
|
|
203
|
-
oqx`name from ${users} where entries(prefs) exists { where $key == "dark"
|
|
203
|
+
oqx`name from ${users} where entries(prefs) exists { where $key == "dark" and $it }`;
|
|
204
204
|
```
|
|
205
205
|
|
|
206
206
|
`entries(array)` yields numeric index keys, a `Map` yields its entries, and
|
|
207
|
-
absence/scalars yield nothing. `$key`
|
|
208
|
-
|
|
207
|
+
absence/scalars yield nothing. An entry's `$key` is its key — and since
|
|
208
|
+
language 0.20 `$key` is the **identity** every row is known by (§9): an
|
|
209
|
+
ordinary row's `$key` is the host's identity, structurally the row itself for
|
|
210
|
+
plain data; an array element has no implicit index — `entries(arr)` is how you
|
|
209
211
|
ask for one. As a plain value (not a source) `entries(x)` is an array of
|
|
210
212
|
`{ key, value }` records.
|
|
211
213
|
|
|
212
214
|
### 3. Predicates (where)
|
|
213
215
|
|
|
214
216
|
`where` filters rows. The predicate language has comparisons (`== != < <= > >=`),
|
|
215
|
-
boolean operators
|
|
217
|
+
boolean operators — the words `and`, `or`, `not`; the symbols `&& || !` are
|
|
218
|
+
accepted synonyms — with grouping `( )`, membership (`in`), arithmetic
|
|
216
219
|
(`+ - * / %`), and bare truthiness. The `where` keyword is always written — a
|
|
217
220
|
leading expression without it is a projection, and a bare comparison in that
|
|
218
221
|
position is an error that points you at `where`.
|
|
@@ -319,7 +322,7 @@ from a row nested one level deeper. A receiver may start with `^` too, which is
|
|
|
319
322
|
how a nested consumer runs over a named root or an enclosing row's relation:
|
|
320
323
|
|
|
321
324
|
```js
|
|
322
|
-
execute("name, peers: ^people collect { name where city == ^city
|
|
325
|
+
execute("name, peers: ^people collect { name where city == ^city and name != ^name } from people", { people });
|
|
323
326
|
```
|
|
324
327
|
|
|
325
328
|
**Correlated subqueries.** Because inner and outer rows often share names, the
|
|
@@ -334,7 +337,7 @@ const family = [
|
|
|
334
337
|
];
|
|
335
338
|
oqx`
|
|
336
339
|
name,
|
|
337
|
-
siblings: ${family} collect { name where parent == ^parent
|
|
340
|
+
siblings: ${family} collect { name where parent == ^parent and name != ^name }
|
|
338
341
|
from ${family}
|
|
339
342
|
`;
|
|
340
343
|
// [{ name: "Ada", siblings: [{ name: "Ben" }] },
|
|
@@ -357,7 +360,7 @@ Methods on a value: `contains`, `startsWith`, `endsWith`, `matches` (regex),
|
|
|
357
360
|
```js
|
|
358
361
|
oqx`name from ${people} where title.startsWith("Eng")`; // → Bob
|
|
359
362
|
oqx`name from ${people} where title.lower() == "director"`; // → Alice
|
|
360
|
-
oqx`name from ${people} where has(age)
|
|
363
|
+
oqx`name from ${people} where has(age) and not has(nickname)`; // present vs absent
|
|
361
364
|
oqx`name from ${people} where tags.contains("admin")`; // array membership
|
|
362
365
|
```
|
|
363
366
|
|
|
@@ -591,8 +594,10 @@ Reached rows expose recursion **intrinsics** in `select` / `order by`:
|
|
|
591
594
|
graph beyond — a boundary or the depth cap), `$ordinal` (a deterministic 1..N
|
|
592
595
|
rank over the walk, ordered by depth then path), and `$stop`
|
|
593
596
|
(`"interior"` / `"leaf"` / `"frontier"` / `"depth"` / `"cycle"`, precedence
|
|
594
|
-
cycle > frontier > depth > leaf > interior — only `interior` rows expand).
|
|
595
|
-
|
|
597
|
+
cycle > frontier > depth > leaf > interior — only `interior` rows expand). A
|
|
598
|
+
brace after a destination opens that destination's **block** (language 0.20),
|
|
599
|
+
whose body has two clauses of its own after `where`: `depth <n>` and
|
|
600
|
+
`frontier <pred>`:
|
|
596
601
|
|
|
597
602
|
```js
|
|
598
603
|
oqx`id, stop: $stop from ${tree} follow children { depth 2 } order by $ordinal`;
|
|
@@ -604,10 +609,10 @@ a nested block (`kids: children collect { … }`) a bare `$depth` is the nested
|
|
|
604
609
|
row's own `$depth` property (absent unless the data carries one), and the
|
|
605
610
|
occurrence's depth is `^$depth`. The same holds everywhere the metadata is not
|
|
606
611
|
present — outside a `follow`, `$depth`/`$ordinal`/`$stop`/`$leaf`/`$frontier`
|
|
607
|
-
are ordinary property reads (a row `{ "$depth": 3 }` projects `3`)
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
612
|
+
are ordinary property reads (a row `{ "$depth": 3 }` projects `3`). `$key` is
|
|
613
|
+
the exception: it is the row's identity everywhere (below). Where the metadata
|
|
614
|
+
exists it wins over a same-named property (language 0.13; before, the six names
|
|
615
|
+
read absent everywhere they were not metadata).
|
|
611
616
|
|
|
612
617
|
The walk is **per-path**: a node reached by N distinct paths yields N
|
|
613
618
|
occurrences, and revisiting an identity already on the current path is admitted
|
|
@@ -616,42 +621,107 @@ runaway. `follow distinct` collapses occurrences to reached nodes (the minimal
|
|
|
616
621
|
`(depth, path)` per identity).
|
|
617
622
|
|
|
618
623
|
**Destinations** (language 0.14) are comma-separated; the walk is their union.
|
|
619
|
-
A **plain destination** is a relation of the current row (`children`, `doc.out
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
624
|
+
A **plain destination** is a relation of the current row (`children`, `doc.out`,
|
|
625
|
+
`entries(kids)`, a tuple `(before, after)` — one destination over the union of
|
|
626
|
+
its elements; an outer reference `^rel` on its own or a literal is a parse
|
|
627
|
+
error). A **destination block** is a receiver followed by a block — `children {
|
|
628
|
+
depth 2 }`, `^people { where manager == ^id }`, `(before, after) { depth 2 }`,
|
|
629
|
+
or with its consumer, `^chain first distinct { … }` — the same directive you
|
|
630
|
+
would write in `select`, re-evaluated per frontier row with `^` bound to that
|
|
631
|
+
row. Its receiver *may* be an outer reference, so a walk can compute successors
|
|
632
|
+
from a named root. Since language 0.20 **a brace after any destination opens
|
|
633
|
+
that destination's block** (there is no options block any more):
|
|
625
634
|
|
|
626
635
|
```js
|
|
627
636
|
const people = [{ id: 1, manager: null }, { id: 2, manager: 1 }, { id: 3, manager: 1 }, { id: 4, manager: 2 }];
|
|
628
|
-
oqx`id values from ${people} where id == 1 follow ^people
|
|
637
|
+
oqx`id values from ${people} where id == 1 follow ^people { where manager == ^id } order by $ordinal`;
|
|
629
638
|
// [1, 2, 3, 4] — the reporting tree under person 1 (`^people` names the root from a top-level walk)
|
|
639
|
+
oqx`id values from ${people} where id == 1 follow ^people { where manager == ^id depth 2 } order by $ordinal`;
|
|
640
|
+
// [1, 2, 3] — `depth` is a clause of the destination block, after its `where`
|
|
630
641
|
|
|
631
642
|
const chain = [{ id: "a" }, { id: "b", prev: "a" }, { id: "c", prev: "b" }];
|
|
632
|
-
oqx`id values from ${chain} where id == "b" follow distinct ^chain
|
|
643
|
+
oqx`id values from ${chain} where id == "b" follow distinct ^chain { where id == ^prev }, ^chain { where prev == ^id } order by $ordinal`;
|
|
633
644
|
// ["b", "a", "c"] — both directions of a chain; a block may order, bound and `distinct` its rows
|
|
634
645
|
```
|
|
635
646
|
|
|
636
|
-
A row's successors are, per destination in order, that destination's rows
|
|
637
|
-
|
|
638
|
-
node reached by two destinations (or twice by one relation) in the
|
|
639
|
-
a single successor
|
|
647
|
+
A row's successors are, per destination in order, that destination's rows
|
|
648
|
+
(shaped by the block's projection, if any), then **unioned by identity within
|
|
649
|
+
the step**: a node reached by two destinations (or twice by one relation) in the
|
|
650
|
+
same step is a single successor, and the destination that reached it first
|
|
651
|
+
governs it. `exists`/`none`/`count` are not destinations (they are
|
|
640
652
|
where-position tests).
|
|
641
653
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
`
|
|
647
|
-
|
|
648
|
-
the
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
654
|
+
A destination block's body is an ordinary block — `where <succ>` (which
|
|
655
|
+
successors keep participating), a projection, `order by`, `limit`/`offset`,
|
|
656
|
+
`distinct` — plus two clauses after `where`: `frontier <pred>` (an occurrence
|
|
657
|
+
this destination reached for which it holds is a frontier — cut, not expanded)
|
|
658
|
+
and `depth <n>` (1–8: this destination expands a frontier row only while its
|
|
659
|
+
`$depth < n`; a destination block's clause alone). The block's `where` is **correlated**: it reads
|
|
660
|
+
the candidate successor's scope whose parent is the frontier row's — a bare name
|
|
661
|
+
is the candidate's own property, `^name` is the row being expanded, `^^name` the
|
|
662
|
+
walk's enclosing scope (language 0.14; before, `^` skipped the frontier row). So
|
|
663
|
+
`follow doc.in { where before.contains(^$path) }` steps only into rows that
|
|
664
|
+
name the row they were reached from. `frontier` is not correlated: it reads the
|
|
665
|
+
occurrence with `^` the enclosing scope, as the body does; seeds are reached
|
|
666
|
+
through no destination and are never frontiers by a destination's rule. **The
|
|
667
|
+
body that carries the `follow` may write `frontier <pred>` too**, after its
|
|
668
|
+
`where` and before `follow`: it applies to the seeds — a matching seed stays in
|
|
669
|
+
the result with `$stop == "frontier"` and is not expanded (`id from tree frontier
|
|
670
|
+
!active follow children { depth 2 }`); read in the seed's scope like the body's
|
|
671
|
+
`where`. Each destination's `frontier` is its own; without a `follow`, a
|
|
672
|
+
body-level `frontier` is a parse error.
|
|
673
|
+
|
|
674
|
+
**Identity is `$key`** (language 0.20). Cycle detection, the per-step union,
|
|
675
|
+
`follow distinct`, `distinct` over unprojected rows and `is` all compare one
|
|
676
|
+
thing — the identity a row is known by — and reading `$key` on any row returns
|
|
677
|
+
it. The default is the host's (`DataContext.identityOf`), else the row's
|
|
678
|
+
**structural value**; `DefaultContext` names none, so over plain objects two
|
|
679
|
+
objects with the same `id` are two nodes. A block may project `$key: <expr>` to
|
|
680
|
+
give the rows it yields that identity — the body's `select` names the seeds, a
|
|
681
|
+
destination block its successors (where `$key:` is not a column; the rows keep
|
|
682
|
+
their shape):
|
|
683
|
+
|
|
684
|
+
```js
|
|
685
|
+
const n1 = { id: 1 }, n2 = { id: 2, next: [n1] }; n1.next = [n2]; // a cyclic graph of fresh references
|
|
686
|
+
oqx`id, stop: $stop, $key: id from ${[n1]} follow next { $key: id } order by $ordinal`;
|
|
687
|
+
// [{ id: 1, stop: "interior", $key: 1 }, { id: 2, stop: "interior", $key: 2 }, { id: 1, stop: "cycle", $key: 1 }]
|
|
688
|
+
oqx`name, $key: email from ${people} follow next { $key: email }`; // known by email at every step
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
Identities are compared as values across the whole walk, so destinations that
|
|
692
|
+
can reach the same rows should project the same `$key`, or a cycle through both
|
|
693
|
+
may run to the depth cap. Give a cyclic graph of fresh objects a `$key` (or a
|
|
694
|
+
context with `identityOf`) rather than rely on the structural default.
|
|
695
|
+
|
|
696
|
+
### 9b. Cardinality and tuples (0.20)
|
|
697
|
+
|
|
698
|
+
In row position an **array** is a collection and a non-array **object** is one
|
|
699
|
+
row: a block over it yields **one record** (or absent, when the row is filtered
|
|
700
|
+
out) rather than a one-element list, `values` the one value, `first`/`single`
|
|
701
|
+
that object — `doc { title }` is `{ title }`, `doc { status == "active" }` is the
|
|
702
|
+
object or absent (a conditional reach-through), `title from doc` the same record.
|
|
703
|
+
A scalar in row position is an eval error (`list(s)` says "one element"
|
|
704
|
+
explicitly); a `from` re-projection or a `follow` is plural. A host says which
|
|
705
|
+
of its objects are collections through `DataContext.isSingle` (a lazy handle);
|
|
706
|
+
by default a non-iterable object is one row.
|
|
707
|
+
|
|
708
|
+
```js
|
|
709
|
+
oqx`${settings} { theme }`; // { theme: "dark" } (one object → one record)
|
|
710
|
+
oqx`${[settings]} { theme }`; // [{ theme: "dark" }] (a collection → a list)
|
|
711
|
+
oqx`from list(${settings})`; // [settings] (the explicit one-element collection)
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
A **tuple** `(e1, e2, …)` — two or more expressions in parentheses — is a value
|
|
715
|
+
and a receiver: its elements concatenated with one level of flattening (a
|
|
716
|
+
collection contributes its items, a row itself; an absent element nothing, a
|
|
717
|
+
`null` is kept; duplicates kept). One parenthesized expression is grouping.
|
|
718
|
+
|
|
719
|
+
```js
|
|
720
|
+
oqx`n: size((tags, aliases)) from ${people}`; // one count over both lists
|
|
721
|
+
oqx`name from ${people} where "x" in (tags, aliases)`; // membership in the union
|
|
722
|
+
oqx`n: (mom, dad, siblings) { name values } from ${people}`; // one block over the union
|
|
723
|
+
oqx`id from ${g} follow (before, after) { depth 2 }`; // one destination over the union
|
|
724
|
+
```
|
|
655
725
|
|
|
656
726
|
### 10. Sugar (0.17)
|
|
657
727
|
|
|
@@ -661,11 +731,15 @@ optimizer and any pushdown planner see it), so nothing here has semantics of its
|
|
|
661
731
|
own except the postfix `!` and the infix `is`:
|
|
662
732
|
|
|
663
733
|
```js
|
|
664
|
-
// A receiver block without a consumer is `collect
|
|
665
|
-
//
|
|
734
|
+
// A receiver block without a consumer is `collect` — wherever a value stands
|
|
735
|
+
// (language 0.20): a select item, a where leaf, a follow destination, a call
|
|
736
|
+
// argument; `x distinct { … }` is `x collect distinct { … }`. A block whose
|
|
737
|
+
// LEADING expression is syntactically a predicate drops `where`.
|
|
666
738
|
oqx`name, current: jobs { !end_date } from ${people}`
|
|
667
739
|
// ≡ name, current: jobs collect { where !end_date } from ${people}
|
|
668
740
|
oqx`${people} { age > 40 }` // ≡ ${people} collect { where age > 40 }
|
|
741
|
+
oqx`n: size(jobs { !end_date }) from ${people}` // a block as a call argument
|
|
742
|
+
oqx`${people} distinct { city }` // ≡ ${people} collect distinct { city }
|
|
669
743
|
oqx`${people} count { jobs exists { } }` // a consumer test is predicate-shaped too
|
|
670
744
|
oqx`${people} first { name }` // a bare name still projects — unchanged
|
|
671
745
|
oqx`${people} first { is active }` // the bare-field filter: `is x` ≡ `!!x`, `not x` ≡ `!x`
|
|
@@ -678,15 +752,15 @@ oqx`name, first_job: jobs[0], primary: jobs[pay > 2]! from ${people}`
|
|
|
678
752
|
// jobs[pay > 2]! ≡ jobs single { where pay > 2 }! (zero or many matches ⇒ an eval error)
|
|
679
753
|
|
|
680
754
|
// Postfix `!` — required: the value, or an eval error naming the expression
|
|
681
|
-
// (and the row's
|
|
755
|
+
// (and the row's `$key` when scalar). Never a filter, never a coercion: 0! and ""! are values.
|
|
682
756
|
oqx`select id!, title from ${docs}` // insist on an identity before a write
|
|
683
757
|
// Tightest precedence: `refs(c)[0]!.name` requires the lookup, `refs(c)[0].name!` the name.
|
|
684
758
|
|
|
685
|
-
// `is` / `is not` compare IDENTITY (
|
|
759
|
+
// `is` / `is not` compare IDENTITY (`$key`: the host's, else structural — §9):
|
|
686
760
|
oqx`name from ${people} where manager is null` // absent (same as == null)
|
|
687
761
|
oqx`${people} count { ^people exists { $it is ^$it } }` // rows compare; `==` leaves that unspecified
|
|
688
|
-
// `and` / `or` are
|
|
689
|
-
// `print` writes the words (language 0.18); `!` stays `!`.
|
|
762
|
+
// `and` / `or` / `not` are the canonical connectives; `&&` / `||` / `!` are exact
|
|
763
|
+
// synonyms (`title or path` coalesces). `print` writes the words (language 0.18); `!` stays `!`.
|
|
690
764
|
|
|
691
765
|
// A select item may use the items to its left (inlined like a `where` alias):
|
|
692
766
|
oqx`boss: ^people[id == ^manager], bossName: boss.name from ${people}`
|
|
@@ -694,7 +768,12 @@ oqx`boss: ^people[id == ^manager], bossName: boss.name from ${people}`
|
|
|
694
768
|
|
|
695
769
|
Reserved words, never a bare field name: `from where select is not and or` and
|
|
696
770
|
the literal words `true false null`. The top level is unchanged — `age > 15
|
|
697
|
-
from ${people}` stays an error, because `where` would precede `from`.
|
|
771
|
+
from ${people}` stays an error, because `where` would precede `from`. One
|
|
772
|
+
carve-out: after a `follow` destination that is a relation of the current row
|
|
773
|
+
(a bare name, a call, a binding) a brace is still the **options** block (`follow
|
|
774
|
+
children { depth 2 }`), never a collect; only over an outer-reference receiver,
|
|
775
|
+
which cannot be a plain destination, is the brace a destination block with the
|
|
776
|
+
implicit `collect` (`follow ^people { where … }`, language 0.19).
|
|
698
777
|
|
|
699
778
|
### Cheat-sheet
|
|
700
779
|
|
|
@@ -702,11 +781,11 @@ from ${people}` stays an error, because `where` would precede `from`.
|
|
|
702
781
|
select … from … where … follow … order by … limit N offset N the fixed clause order (each at most once)
|
|
703
782
|
name, alias: expr, nested: rel collect { … } projection (`select` may be dropped only here, in first position)
|
|
704
783
|
from ${source} source collection (required at the top level)
|
|
705
|
-
where a == b
|
|
784
|
+
where a == b and rel exists { where … } or not c predicate tree + nested ops (`&&`/`||`/`!` are accepted synonyms)
|
|
706
785
|
rel { pay > 2 } / rel { employer } a block without a consumer is collect; a leading predicate is where-first, a bare name projects
|
|
707
786
|
rel[p] / rel[p]! / rel[0] / rel[0]! first match / exactly one (required) / positional / required positional
|
|
708
787
|
x! required: the value, or an error when absent (tightest; `a!.b` vs `a.b!`)
|
|
709
|
-
is x / not x / x is y / x is not y truthiness (≡ !!x / !x) / identity (
|
|
788
|
+
is x / not x / x is y / x is not y truthiness (≡ !!x / !x) / identity ($key: the host's, else structural; absent ≡ null)
|
|
710
789
|
where alias / a: expr, b: a `where` and later items may use this body's select aliases (resolved before evaluation; an alias shadows a field)
|
|
711
790
|
where rel none { … } zero rows (≡ !rel exists { … }; "all" = none over the complement)
|
|
712
791
|
where x in lo..hi / lo...hi / ..hi / lo.. range membership (incl. / excl. / open-ended)
|
|
@@ -721,9 +800,12 @@ from entries(obj) … $key / $it a record's properties as a co
|
|
|
721
800
|
entries(rel) exists { … } a free-function call may be a receiver
|
|
722
801
|
order by expr desc, expr2 ordering
|
|
723
802
|
limit n / offset n bound the row set (after where/order/distinct, before the consumer)
|
|
724
|
-
follow rel { where … frontier … depth n
|
|
725
|
-
follow before, after
|
|
726
|
-
follow ^people
|
|
803
|
+
follow rel { where … frontier … depth n } recursion ($depth/$stop/$leaf/$frontier/$key); the brace is the destination's block, `^name` in its where the frontier row
|
|
804
|
+
follow before, after / follow (before, after) several destinations: the union of their rows (per step, by identity) / one tuple destination
|
|
805
|
+
follow ^people { where manager == ^id } a destination block: successors computed per frontier row (`^` = that row; `collect` implicit, 0.20)
|
|
806
|
+
$key / $key: expr the identity a row is known by (the host's, else structural) / give the rows a block yields that identity
|
|
807
|
+
(a, b) / rel distinct { … } a tuple: the elements, one level flat (a value and a receiver) / collect distinct (0.20)
|
|
808
|
+
obj { title } one object → one record (or absent); a collection → a list; a scalar → an error (0.20)
|
|
727
809
|
${source} <collect|exists|none|count|first|single> { … } whole-query consumer
|
|
728
810
|
```
|
|
729
811
|
|
|
@@ -836,9 +918,12 @@ than approximate it. The conformance suite verifies this.
|
|
|
836
918
|
|
|
837
919
|
The engine never touches host objects directly; it asks a `DataContext` to
|
|
838
920
|
resolve named roots, read properties/relations, coerce results to rows, and
|
|
839
|
-
|
|
921
|
+
optionally to name a row's identity (`identityOf` — `$key`; the default is the
|
|
922
|
+
row's structural value), to say which of its objects are collections rather
|
|
923
|
+
than one row (`isSingle`; by default a non-iterable object is one row, so a
|
|
924
|
+
block over it is one record), and to supply a `rootObject()` — the root
|
|
840
925
|
scope's row, what `$it` is at the root and `^$it` from a top-level row; without
|
|
841
|
-
one the root row is absent
|
|
926
|
+
one the root row is absent. Implement it to query an ORM graph, a remote API,
|
|
842
927
|
or lazily loaded relations — the query *semantics* stay in OQX. Name resolution is simple
|
|
843
928
|
for a context: a bare `field`, a `.field` segment, and a `^field` outer
|
|
844
929
|
reference each become one `get(row, key)` against exactly the row of the scope
|
|
@@ -851,7 +936,7 @@ const graph = {
|
|
|
851
936
|
root: (name) => name === "tree" ? [nodes.get(1)] : undefined,
|
|
852
937
|
get: (row, key) => key === "children" ? row.childIds.map(id => nodes.get(id)) : row[key],
|
|
853
938
|
toRows: (v) => v == null ? [] : Array.isArray(v) ? v : [v],
|
|
854
|
-
|
|
939
|
+
identityOf: (row) => row.id, // `$key`: for follow dedup, distinct, `is` (optional; default: structural)
|
|
855
940
|
};
|
|
856
941
|
run(parse("id, depth: $depth from tree follow children"), { context: graph });
|
|
857
942
|
```
|
|
@@ -884,7 +969,7 @@ const ctx = {
|
|
|
884
969
|
return builtins.callMethod(name, recv, args);
|
|
885
970
|
},
|
|
886
971
|
};
|
|
887
|
-
run(parse("id from tree where age(born) > 18
|
|
972
|
+
run(parse("id from tree where age(born) > 18 and title.slug() == 'x'"), { context: ctx });
|
|
888
973
|
```
|
|
889
974
|
|
|
890
975
|
The simplest route is to `extends DefaultContext` and `super.callFunction(...)`
|
|
@@ -910,7 +995,7 @@ import { parse, PlannedEngine } from "@omgbase/oqx";
|
|
|
910
995
|
import { SqliteTable } from "@omgbase/oqx/sqlite";
|
|
911
996
|
|
|
912
997
|
const planner = new SqliteTable(db, "emp", { columns: ["id", "name", "dept", "level"] });
|
|
913
|
-
new PlannedEngine(planner).run(parse('name from emp where dept == "eng"
|
|
998
|
+
new PlannedEngine(planner).run(parse('name from emp where dept == "eng" and level >= 5'), []);
|
|
914
999
|
// → `dept`/`level` pushed to SQL; anything untranslatable finishes in-memory
|
|
915
1000
|
```
|
|
916
1001
|
|
|
@@ -941,7 +1026,7 @@ results and on errors.
|
|
|
941
1026
|
|
|
942
1027
|
**What is optimized**
|
|
943
1028
|
|
|
944
|
-
- **Correlated equality → hash probe.** A top-level
|
|
1029
|
+
- **Correlated equality → hash probe.** A top-level `and` conjunct of the block's
|
|
945
1030
|
`where` of the form `local == outer` (either side) — `local` a bare identifier
|
|
946
1031
|
or member chain on the block's row (`customer_id`, `meta.id`, `$it`),
|
|
947
1032
|
`outer` anything that reads nothing from that row (`^customer_id`, `^^x.id`,
|
|
@@ -963,7 +1048,7 @@ results and on errors.
|
|
|
963
1048
|
by`) are answered from the bucket's size after the bound without entering a
|
|
964
1049
|
row.
|
|
965
1050
|
|
|
966
|
-
**The sound rule.**
|
|
1051
|
+
**The sound rule.** `and` is strictly left to right and short-circuiting (§13),
|
|
967
1052
|
so a probe may skip a row only if everything the scan would have evaluated for
|
|
968
1053
|
it could neither raise nor bind: an equality is hoisted only when **every
|
|
969
1054
|
conjunct to its left is raise-free** — no function or method call, no `single`,
|
|
@@ -973,7 +1058,7 @@ too. A conjunct that could raise *right* of the equality is simply evaluated
|
|
|
973
1058
|
over the bucket, where the scan would have evaluated it on the same rows. A
|
|
974
1059
|
block whose `where` lifts anywhere is never probed (a lift could change what
|
|
975
1060
|
the outer side reads between rows). Otherwise the block scans as before, so
|
|
976
|
-
`where nope(x)
|
|
1061
|
+
`where nope(x) and id == ^id` still raises `unknown function`.
|
|
977
1062
|
|
|
978
1063
|
Property reads are treated as total (§23: evaluation is otherwise total). A
|
|
979
1064
|
`DataContext.get` that throws is caught where the engine relies on reads it
|
|
@@ -1082,7 +1167,7 @@ adapter lives on the `@omgbase/oqx/sqlite` subpath.
|
|
|
1082
1167
|
(the optimizer rules for nested blocks, default `DEFAULT_RULES`; `[]` is the
|
|
1083
1168
|
naive scan) and `trace` (a `(event: TraceEvent) => void` sink for index /
|
|
1084
1169
|
probe / memo / cardinality / fallback events). See [Performance](#performance-relational-patterns).
|
|
1085
|
-
- `DataContext` — the tier-2 interface: `root`, `get`, `toRows`, `
|
|
1170
|
+
- `DataContext` — the tier-2 interface: `root`, `get`, `toRows`, `identityOf`, `isSingle`,
|
|
1086
1171
|
plus optional `callFunction` / `callMethod` (above), an optional
|
|
1087
1172
|
`indexFor(collection, path)` returning a `RowIndex` for a pre-built equality
|
|
1088
1173
|
index (`lookup` → positions; optionally `lookupRows` → rows, probed without
|
|
@@ -1095,7 +1180,7 @@ adapter lives on the `@omgbase/oqx/sqlite` subpath.
|
|
|
1095
1180
|
"native")`; `BUILTIN_METHODS.matches` is always the baseline.
|
|
1096
1181
|
- `CallResult` — `{ handled: boolean; value?: unknown }`, returned by those hooks.
|
|
1097
1182
|
- `DefaultContext` — `new DefaultContext(roots?, options?)`; plain-object
|
|
1098
|
-
access,
|
|
1183
|
+
access, structural identity (`$key` is the row itself unless projected), and the builtin function/method tables. `options`:
|
|
1099
1184
|
`{ regexDialect?: RegexDialect }` (see the regex baseline, §4).
|
|
1100
1185
|
- `compileRegex(pattern, flags?, dialect?)` — the `matches()` compiler: a
|
|
1101
1186
|
`RegExp`, or an `OqxError` for a bad pattern/flag. `RegexDialect` /
|
|
@@ -1142,7 +1227,7 @@ adapter lives on the `@omgbase/oqx/sqlite` subpath.
|
|
|
1142
1227
|
import { parse, run, IndexedCollection, PlannedEngine } from "@omgbase/oqx";
|
|
1143
1228
|
const planner = new IndexedCollection("people", people, ["city", "title"]);
|
|
1144
1229
|
const engine = new PlannedEngine(planner, planner.context());
|
|
1145
|
-
run(parse('name from people where city == "NYC"
|
|
1230
|
+
run(parse('name from people where city == "NYC" and age > 30'), { engine });
|
|
1146
1231
|
// city probe from the index; `age > 30` finished in-memory over the candidates
|
|
1147
1232
|
```
|
|
1148
1233
|
|
package/dist/ast.d.ts
CHANGED
|
@@ -76,6 +76,10 @@ export type Expr = {
|
|
|
76
76
|
lo: Expr | null;
|
|
77
77
|
hi: Expr | null;
|
|
78
78
|
exclusiveEnd: boolean;
|
|
79
|
+
} | {
|
|
80
|
+
kind: "tuple";
|
|
81
|
+
span: Span;
|
|
82
|
+
items: Expr[];
|
|
79
83
|
} | OpNode;
|
|
80
84
|
/** One `order by` term. */
|
|
81
85
|
export interface OrderSpec {
|
|
@@ -123,16 +127,13 @@ export interface OpNode {
|
|
|
123
127
|
* destination block — a select-position directive (`collect`/`first`/`single`) re-evaluated
|
|
124
128
|
* per frontier row, inside which `^` is that row. Discriminate on `kind === "op"`. */
|
|
125
129
|
export type FollowDestination = Expr | OpNode;
|
|
126
|
-
/** The recursive `follow` clause.
|
|
130
|
+
/** The recursive `follow` clause (since 0.20 just the destinations: a
|
|
131
|
+
* destination's `where`, `frontier` and `depth` live in its own block). */
|
|
127
132
|
export interface Follow {
|
|
128
133
|
kind: "follow";
|
|
129
134
|
span: Span;
|
|
130
135
|
destinations: FollowDestination[];
|
|
131
136
|
distinct: boolean;
|
|
132
|
-
where: Expr | null;
|
|
133
|
-
frontier: Expr | null;
|
|
134
|
-
depth: number | null;
|
|
135
|
-
by: Expr | null;
|
|
136
137
|
}
|
|
137
138
|
/** A nested (sub)query body — the `{ … }` of a directive. */
|
|
138
139
|
export interface Subquery {
|
|
@@ -143,6 +144,16 @@ export interface Subquery {
|
|
|
143
144
|
select: SelectItem[];
|
|
144
145
|
orderBy: OrderSpec[] | null;
|
|
145
146
|
follow: Follow | null;
|
|
147
|
+
/** `frontier P` / `depth n` (since 0.20). In a `follow` destination block
|
|
148
|
+
* `frontier` marks the occurrences reached through this destination for
|
|
149
|
+
* which `P` holds (not expanded; `P` read one level out, under the walk's
|
|
150
|
+
* enclosing scope), and `depth n` makes the destination expand a frontier
|
|
151
|
+
* row only while that row's `$depth < n` (1..8; `null` = the hard cap 8).
|
|
152
|
+
* In any other block `frontier` is legal only with a `follow` and marks the
|
|
153
|
+
* walk's SEEDS, read in the seed's scope like `where` (as on `Query`);
|
|
154
|
+
* `depth` is destination-only. Both `null` when absent. */
|
|
155
|
+
frontier: Expr | null;
|
|
156
|
+
depth: number | null;
|
|
146
157
|
/** `values` — scalar projection mode: the (single) projected expression is the
|
|
147
158
|
* row's result itself rather than being wrapped in a `{ name: value }` record,
|
|
148
159
|
* so `name values` yields `["Bob", …]` and `$it values` yields the rows. */
|
|
@@ -186,6 +197,11 @@ export interface Query {
|
|
|
186
197
|
orderBy: OrderSpec[] | null;
|
|
187
198
|
consumer: Consumer;
|
|
188
199
|
follow: Follow | null;
|
|
200
|
+
/** `frontier P` (since 0.20) — legal only when the body has a `follow`: a
|
|
201
|
+
* seed for which `P` holds (read in the seed's own scope, `^` the enclosing
|
|
202
|
+
* scope, like `where`) is a frontier (`$stop == "frontier"`, not expanded).
|
|
203
|
+
* `null` without one. */
|
|
204
|
+
frontier: Expr | null;
|
|
189
205
|
/** `distinct` — dedup the result rows by their projected value (see OpNode). */
|
|
190
206
|
distinct: boolean;
|
|
191
207
|
/** `values` — scalar projection mode (see Subquery). */
|
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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// empty span `[0, 0]` (see `build.ts`).
|
|
13
13
|
/** The expression kinds (`Expr["kind"]`), for `isExpr`. */
|
|
14
14
|
const EXPR_KINDS = new Set([
|
|
15
|
-
"lit", "ident", "outer", "binding", "member", "call", "unary", "required", "binary", "logical", "in", "range",
|
|
15
|
+
"lit", "ident", "outer", "binding", "member", "call", "unary", "required", "binary", "logical", "in", "range", "tuple",
|
|
16
16
|
]);
|
|
17
17
|
/** Whether a node is a scalar `Expr` (an `op` in expression position is not: it
|
|
18
18
|
* 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;
|
|
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;AAsLxC,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;CACvH,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
|
@@ -16,6 +16,8 @@ export declare const binary: (op: BinaryOp, left: Expr, right: Expr) => Expr;
|
|
|
16
16
|
export declare const logical: (op: LogicalOp, left: Expr, right: Expr) => Expr;
|
|
17
17
|
export declare const inOp: (left: Expr, right: Expr) => Expr;
|
|
18
18
|
export declare const range: (lo: Expr | null, hi: Expr | null, exclusiveEnd?: boolean) => Expr;
|
|
19
|
+
/** `(e1, e2, …)` — a tuple of two or more expressions (SEMANTICS §3b). */
|
|
20
|
+
export declare const tuple: (items: Expr[]) => Expr;
|
|
19
21
|
export declare const scalar: (expr: Expr) => Where;
|
|
20
22
|
export declare const and: (parts: Where[]) => Where;
|
|
21
23
|
export declare const or: (parts: Where[]) => Where;
|
|
@@ -30,7 +32,11 @@ export declare function op(receiver: Expr, consumer: Consumer, sub?: Subquery, o
|
|
|
30
32
|
distinct?: boolean;
|
|
31
33
|
countCmp?: CountCmp | null;
|
|
32
34
|
}): OpNode;
|
|
33
|
-
|
|
35
|
+
/** `follow [distinct] <destinations>`; a destination's `where`/`frontier`/`depth`
|
|
36
|
+
* belong to its block (`op(recv, "collect", subquery({ depth: 2 }))`). */
|
|
37
|
+
export declare function follow(destinations: FollowDestination[], opts?: {
|
|
38
|
+
distinct?: boolean;
|
|
39
|
+
}): Follow;
|
|
34
40
|
/** A top-level query over `source`; every other clause defaults to empty/absent
|
|
35
41
|
* and the consumer to `collect`. */
|
|
36
42
|
export declare function query(source: Expr, parts?: Partial<Omit<Query, "kind" | "span" | "source">>): Query;
|
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,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EACpH,QAAQ,EAAE,OAAO,EAAE,KAAK,EACzB,MAAM,UAAU,CAAC;AAElB,mEAAmE;AACnE,eAAO,MAAM,IAAI,QAAO,IAAc,CAAC;AAEvC,eAAO,MAAM,GAAG,GAAI,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,KAAG,IAA8C,CAAC;AAC7G,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,KAAG,IAA+C,CAAC;AACrF,eAAO,MAAM,KAAK,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,KAAG,IAAuD,CAAC;AAC7G,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,IAAkD,CAAC;AAC3F,eAAO,MAAM,MAAM,GAAI,MAAM,IAAI,EAAE,MAAM,MAAM,KAAG,IAAsD,CAAC;AACzG,kCAAkC;AAClC,eAAO,MAAM,IAAI,GAAI,OAAO,MAAM,EAAE,GAAG,MAAM,MAAM,EAAE,KAAG,IAA+D,CAAC;AACxH,eAAO,MAAM,IAAI,GAAI,MAAM,IAAI,GAAG,IAAI,EAAE,MAAM,MAAM,EAAE,OAAM,IAAI,EAAO,KAAG,IAA0D,CAAC;AACrI,eAAO,MAAM,KAAK,GAAI,IAAI,OAAO,EAAE,MAAM,IAAI,KAAG,IAAmD,CAAC;AACpG,oDAAoD;AACpD,eAAO,MAAM,QAAQ,GAAI,MAAM,IAAI,KAAG,IAAkD,CAAC;AACzF,eAAO,MAAM,MAAM,GAAI,IAAI,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,IAAI,KAAG,IAA2D,CAAC;AAC3H,eAAO,MAAM,OAAO,GAAI,IAAI,SAAS,EAAE,MAAM,IAAI,EAAE,OAAO,IAAI,KAAG,IAA4D,CAAC;AAC9H,eAAO,MAAM,IAAI,GAAI,MAAM,IAAI,EAAE,OAAO,IAAI,KAAG,IAAmD,CAAC;AACnG,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,GAAG,IAAI,EAAE,sBAAoB,KAAG,IAA+D,CAAC;
|
|
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,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EACpH,QAAQ,EAAE,OAAO,EAAE,KAAK,EACzB,MAAM,UAAU,CAAC;AAElB,mEAAmE;AACnE,eAAO,MAAM,IAAI,QAAO,IAAc,CAAC;AAEvC,eAAO,MAAM,GAAG,GAAI,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,KAAG,IAA8C,CAAC;AAC7G,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,KAAG,IAA+C,CAAC;AACrF,eAAO,MAAM,KAAK,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,KAAG,IAAuD,CAAC;AAC7G,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,IAAkD,CAAC;AAC3F,eAAO,MAAM,MAAM,GAAI,MAAM,IAAI,EAAE,MAAM,MAAM,KAAG,IAAsD,CAAC;AACzG,kCAAkC;AAClC,eAAO,MAAM,IAAI,GAAI,OAAO,MAAM,EAAE,GAAG,MAAM,MAAM,EAAE,KAAG,IAA+D,CAAC;AACxH,eAAO,MAAM,IAAI,GAAI,MAAM,IAAI,GAAG,IAAI,EAAE,MAAM,MAAM,EAAE,OAAM,IAAI,EAAO,KAAG,IAA0D,CAAC;AACrI,eAAO,MAAM,KAAK,GAAI,IAAI,OAAO,EAAE,MAAM,IAAI,KAAG,IAAmD,CAAC;AACpG,oDAAoD;AACpD,eAAO,MAAM,QAAQ,GAAI,MAAM,IAAI,KAAG,IAAkD,CAAC;AACzF,eAAO,MAAM,MAAM,GAAI,IAAI,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,IAAI,KAAG,IAA2D,CAAC;AAC3H,eAAO,MAAM,OAAO,GAAI,IAAI,SAAS,EAAE,MAAM,IAAI,EAAE,OAAO,IAAI,KAAG,IAA4D,CAAC;AAC9H,eAAO,MAAM,IAAI,GAAI,MAAM,IAAI,EAAE,OAAO,IAAI,KAAG,IAAmD,CAAC;AACnG,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,GAAG,IAAI,EAAE,sBAAoB,KAAG,IAA+D,CAAC;AAC/I,0EAA0E;AAC1E,eAAO,MAAM,KAAK,GAAI,OAAO,IAAI,EAAE,KAAG,IAAgD,CAAC;AAEvF,eAAO,MAAM,MAAM,GAAI,MAAM,IAAI,KAAG,KAAiD,CAAC;AACtF,eAAO,MAAM,GAAG,GAAI,OAAO,KAAK,EAAE,KAAG,KAA+C,CAAC;AACrF,eAAO,MAAM,EAAE,GAAI,OAAO,KAAK,EAAE,KAAG,KAA8C,CAAC;AACnF,eAAO,MAAM,GAAG,GAAI,MAAM,KAAK,KAAG,KAA8C,CAAC;AAEjF,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,EAAE,MAAM,IAAI,EAAE,aAAQ,KAAG,UAAiE,CAAC;AAC7H,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,IAAI,MAAM,KAAG,UAA2D,CAAC;AAC/G,eAAO,MAAM,KAAK,GAAI,MAAM,IAAI,EAAE,cAAY,KAAG,SAA0D,CAAC;AAE5G,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,KAAK,GAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,CAAM,GAAG,QAAQ,CAMvF;AAED,wDAAwD;AACxD,wBAAgB,EAAE,CAChB,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,GAAE,QAAqB,EAC9D,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAO,GAC5D,MAAM,CAER;AAED;0EAC0E;AAC1E,wBAAgB,MAAM,CAAC,YAAY,EAAE,iBAAiB,EAAE,EAAE,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CAEnG;AAED;oCACoC;AACpC,wBAAgB,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,GAAE,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAM,GAAG,KAAK,CAOvG"}
|