@omgbase/core 0.28.0 → 0.29.2
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/README.md +7 -5
- package/dist/src/core/read/document.d.ts +11 -10
- package/dist/src/core/read/document.js +12 -11
- package/dist/src/core/read/nodes.d.ts +10 -10
- package/dist/src/core/read/nodes.js +11 -11
- package/dist/src/core/read/reader.d.ts +37 -24
- package/dist/src/core/read/reader.js +44 -31
- package/dist/src/graph/history.d.ts +25 -22
- package/dist/src/graph/history.js +17 -17
- package/dist/src/graph/inbound-links.d.ts +7 -7
- package/dist/src/graph/inbound-links.js +7 -7
- package/dist/src/graph/link-health.d.ts +9 -9
- package/dist/src/graph/link-health.js +9 -9
- package/dist/src/graph/links.d.ts +3 -3
- package/dist/src/graph/links.js +4 -4
- package/dist/src/mcp/arguments.d.ts +10 -3
- package/dist/src/mcp/arguments.js +32 -9
- package/dist/src/mcp/errors.d.ts +1 -1
- package/dist/src/mcp/graph.d.ts +22 -22
- package/dist/src/mcp/graph.js +54 -47
- package/dist/src/mcp/reference.d.ts +29 -1
- package/dist/src/mcp/reference.js +398 -255
- package/dist/src/mcp/server.d.ts +9 -0
- package/dist/src/mcp/server.js +170 -108
- package/dist/src/mutate/apply.d.ts +6 -6
- package/dist/src/mutate/apply.js +1 -1
- package/dist/src/mutate/canonical.d.ts +1 -1
- package/dist/src/mutate/canonical.js +2 -2
- package/dist/src/mutate/commit.d.ts +3 -3
- package/dist/src/mutate/commit.js +4 -4
- package/dist/src/mutate/docs.d.ts +1 -1
- package/dist/src/mutate/docs.js +8 -8
- package/dist/src/mutate/effects.d.ts +17 -17
- package/dist/src/mutate/effects.js +113 -113
- package/dist/src/mutate/macros.d.ts +5 -5
- package/dist/src/mutate/macros.js +1 -1
- package/dist/src/mutate/materialize.js +2 -2
- package/dist/src/mutate/ops.js +2 -2
- package/dist/src/mutate/opset.d.ts +7 -7
- package/dist/src/mutate/plan-update.js +2 -2
- package/dist/src/mutate/plan.d.ts +8 -8
- package/dist/src/mutate/plan.js +25 -25
- package/dist/src/mutate/select.d.ts +4 -4
- package/dist/src/mutate/select.js +57 -56
- package/dist/src/mutate/sugar.d.ts +10 -10
- package/dist/src/mutate/sugar.js +21 -21
- package/dist/src/oqx-js/context.d.ts +8 -4
- package/dist/src/oqx-js/context.js +206 -189
- package/dist/src/oqx-js/names.d.ts +28 -17
- package/dist/src/oqx-js/names.js +115 -78
- package/dist/src/oqx-js/planner.d.ts +4 -8
- package/dist/src/oqx-js/planner.js +6 -10
- package/dist/src/oqx-js/root-row.d.ts +6 -5
- package/dist/src/oqx-js/root-row.js +15 -16
- package/dist/src/oqx-js/run.d.ts +18 -17
- package/dist/src/oqx-js/run.js +57 -54
- package/dist/src/oqx-js/sql/translate.d.ts +5 -5
- package/dist/src/oqx-js/sql/translate.js +162 -161
- package/dist/src/oqx-js/store-index.d.ts +1 -1
- package/dist/src/oqx-js/store-index.js +28 -30
- package/dist/src/search/external.d.ts +10 -0
- package/dist/src/search/external.js +20 -2
- package/dist/src/search/resolve.d.ts +2 -2
- package/dist/src/search/resolve.js +1 -1
- package/dist/src/search/rrf.js +2 -2
- package/dist/src/search/text.d.ts +5 -5
- package/dist/src/search/text.js +2 -2
- package/dist/src/spec-versions.d.ts +7 -7
- package/dist/src/spec-versions.js +6 -6
- package/dist/src/surface-paths.d.ts +18 -17
- package/dist/src/surface-paths.js +23 -21
- package/dist/src/sync/admin.d.ts +2 -2
- package/dist/src/sync/admin.js +1 -1
- package/package.json +2 -2
|
@@ -5,233 +5,269 @@
|
|
|
5
5
|
// tool handler stays thin. Semantics are the @omgbase/oqx contract (ADR-013);
|
|
6
6
|
// when this text and the corpus (corpus/oqx/alchemy.test.ts) disagree, the
|
|
7
7
|
// corpus wins.
|
|
8
|
+
//
|
|
9
|
+
// Three texts (spec/surface §4 `query_syntax`, 5.1): `QUERY_SYNTAX_CARD` is
|
|
10
|
+
// what `query_syntax {}` returns — a ~3 kB card an agent can hold in context
|
|
11
|
+
// (the 2026-10-21 agent-bench fetched the 38 kB manual twelve times and
|
|
12
|
+
// replayed it as cached prefix on every later turn); `QUERY_SYNTAX` is the
|
|
13
|
+
// whole manual (`section: "all"`); `querySyntaxSection(slug)` is one chapter of
|
|
14
|
+
// it, by the slugs of `QUERY_SYNTAX_SECTIONS` (the manual's `##` headings). The
|
|
15
|
+
// Rust twin (`crates/omgbase-surface/src/reference.rs`) mirrors all three byte
|
|
16
|
+
// for byte; its tests compare against this file.
|
|
8
17
|
export const QUERY_SYNTAX = `# query — OQX syntax reference
|
|
9
18
|
|
|
10
19
|
The \`query\` tool takes ONE plain OQX string (+ optional \`limit\`/\`cursor\`, and
|
|
11
|
-
\`bare: true\` to drop the automatic \`
|
|
20
|
+
\`bare: true\` to drop the automatic \`_id\`/\`_path\` envelope — needs a \`select\`).
|
|
12
21
|
There is no {from, filter, order} envelope: every concern is a clause of the
|
|
13
22
|
string. Two rules tell the two syntaxes apart: DOT navigation belongs to the
|
|
14
|
-
host object model (\`
|
|
23
|
+
host object model (\`_doc.layer\`, \`_section._blocks\`, \`_dst._title\`); WHITESPACE
|
|
15
24
|
query directives belong to OQX — a nested query is \`<receiver> collect|exists|
|
|
16
25
|
count|first|single { <block> }\` (a postfix directive + a \`{ … }\` block), never a
|
|
17
26
|
method. Its distinctive power is the receiver-constrained nested query that
|
|
18
|
-
correlates to the current row: \`
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
[select <items>]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<target> count|exists|none|first|single { <block> } (scalar
|
|
25
|
-
|
|
27
|
+
correlates to the current row: \`docs { _nodes exists { _kind == "md:task" } }\`
|
|
28
|
+
returns only the docs that themselves contain a matching node.
|
|
29
|
+
|
|
30
|
+
<target> { [select <items>] [where <pred>] [frontier <pred>] [follow <dest> [{ … }], …]
|
|
31
|
+
[flatten <expr> [{ … }] [as <name>]]… [group by <key>, …] [having <pred>]
|
|
32
|
+
[order by <expr> [asc|desc], …] [limit N] [offset N] }
|
|
33
|
+
<target> count|exists|none|first|single { <block> } (a scalar or one row; <target> is docs, blocks, nodes, links or revisions)
|
|
34
|
+
|
|
35
|
+
THE COLLECTION HEADS THE BODY (OQX 0.26): \`docs { … }\` reads the documents and
|
|
36
|
+
shapes them with the clauses inside the braces; a consumer word between the
|
|
37
|
+
target and the brace (\`docs count { … }\`) shapes the whole result; nothing may
|
|
38
|
+
follow the closing brace. The pre-0.26 clause form \`select … from docs where …\`
|
|
39
|
+
is still read as the same query — write the braces.
|
|
26
40
|
CLAUSE ORDER IS FIXED: select, from, where, frontier, follow, flatten, group by, having, order by, limit, offset —
|
|
27
|
-
each at most once (\`flatten\` may repeat
|
|
41
|
+
each at most once (\`flatten\` may repeat; \`from\` inside a body RE-PROJECTS the
|
|
42
|
+
rows through a relation, see Nested — it never names the source); an
|
|
43
|
+
out-of-order clause is a parse error naming the order.
|
|
28
44
|
Only \`select\` may drop its keyword, and only as the first clause
|
|
29
|
-
(\`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
\`
|
|
34
|
-
\`docs count { … }\`). \`where\` and later \`select\` items may use the same
|
|
35
|
-
\`select\` aliases (\`select
|
|
36
|
-
shadows a same-named field there). \`and\` / \`or\` / \`not\` are the connectives;
|
|
45
|
+
(\`docs { _path, era where … }\`). A body may LEAD with a predicate — \`docs { era
|
|
46
|
+
> 1600 }\` filters (see Sugar) — but a predicate after a projection still needs
|
|
47
|
+
the keyword (\`{ _name where _kind == "md:task" }\`, not \`{ _name, _kind ==
|
|
48
|
+
"md:task" }\`); in the clause form a top-level predicate needs \`where\` (\`era >
|
|
49
|
+
1600 from docs\` is the clause-order error), and \`from docs count\` is an error
|
|
50
|
+
(write \`docs count { … }\`). \`where\` and later \`select\` items may use the same
|
|
51
|
+
body's \`select\` aliases (\`docs { select _path, old: era < 1000 where old }\`; an
|
|
52
|
+
alias shadows a same-named field there). \`and\` / \`or\` / \`not\` are the connectives;
|
|
37
53
|
the symbols \`&&\` / \`||\` / \`!\` are accepted synonyms (the words are canonical and
|
|
38
54
|
what \`print\` writes).
|
|
39
55
|
|
|
40
56
|
## Sugar (OQX 0.17)
|
|
41
57
|
|
|
42
58
|
Every form here is shorthand for an explicit directive (the AST is the explicit form):
|
|
43
|
-
nodes {
|
|
59
|
+
nodes { _kind == "md:task" } ≡ nodes collect { where _kind == "md:task" } — a receiver block with
|
|
44
60
|
no consumer is collect; a block whose LEADING expression is a predicate
|
|
45
61
|
(comparison, call, !/is/not, in, literal, (…), a consumer test — anything
|
|
46
|
-
but a bare name, a dotted path or a ^lift) is where-first.
|
|
47
|
-
project:
|
|
48
|
-
|
|
49
|
-
size(
|
|
50
|
-
follow destination too: follow
|
|
62
|
+
but a bare name, a dotted path or a ^lift) is where-first. Names still
|
|
63
|
+
project: _nodes { _name } ≡ _nodes collect { _name }; filter a bare field with
|
|
64
|
+
_nodes { is checked }. Wherever a value stands (0.20), call arguments included:
|
|
65
|
+
size(_children { is checked }); x distinct { … } ≡ x collect distinct { … }. After a
|
|
66
|
+
follow destination too: follow _children { depth 2 } is that destination's block.
|
|
51
67
|
(a, b) a tuple (0.20): two or more expressions, concatenated one level flat — a value
|
|
52
68
|
(size((tags, aliases)), "x" in (tags, aliases)) and a receiver ((a, b) { … },
|
|
53
69
|
follow (before, after) { depth 2 }); (a) alone is grouping
|
|
54
70
|
refs(x)[0] ≡ refs(x) first { offset 0 } positional (integer literal or \${binding});
|
|
55
|
-
out of range ⇒ absent; refs(x)[0].
|
|
56
|
-
^docs[
|
|
57
|
-
^docs[
|
|
58
|
-
|
|
71
|
+
out of range ⇒ absent; refs(x)[0]._path navigates the row
|
|
72
|
+
^docs[_path == ^company] ≡ ^docs first { where _path == ^company } first match or absent
|
|
73
|
+
^docs[_path == ^company]! ≡ … single { … } required: exactly one, else filter_invalid
|
|
74
|
+
_title! required: _title, or an error naming the expression (and the row's $key — its _id)
|
|
59
75
|
— never a filter, never a coercion (0!, ""! are values); tightest: a!.b vs a.b!
|
|
60
76
|
is x / not x truthiness of x, and its negation (prefix; \`not\` ≡ \`!\`)
|
|
61
77
|
x is y / x is not y identity ($key: a store row's id, else structural) — compares rows, which == does not;
|
|
62
78
|
x is null = absent; for scalars is ≡ ==; comparison precedence, no chaining
|
|
63
|
-
a and b / a or b the connectives (≡ && / ||: same precedence, short-circuit, value:
|
|
64
|
-
boss: ^docs[
|
|
79
|
+
a and b / a or b the connectives (≡ && / ||: same precedence, short-circuit, value: title or _title coalesces)
|
|
80
|
+
boss: ^docs[_path == ^manager], bossName: boss._title a select item may use the items to its LEFT
|
|
65
81
|
Reserved words (never a bare field name): from where select is not and or, true false null.
|
|
66
82
|
Contextual words (limit order offset follow collect count first distinct desc by in frontier depth group having
|
|
67
|
-
flatten as) parse as fields in expression position (select o: order
|
|
83
|
+
flatten as) parse as fields in expression position (docs { select o: order }).
|
|
68
84
|
|
|
69
|
-
Results are LEAN hits — {
|
|
85
|
+
Results are LEAN hits — { _id, _path } + whatever \`select\` projects (or \`values\`,
|
|
70
86
|
\`count\`, \`exists\`, \`none\`). The envelope keys ARE the attributes: copy a hit's
|
|
71
|
-
\`
|
|
72
|
-
the envelope (no duplicate columns);
|
|
73
|
-
|
|
74
|
-
\`
|
|
75
|
-
surfacing as a value (an empty \`collect { }\`) is the same \`{
|
|
76
|
-
other tool spells a row the same way (\`docs_read\` → \`{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
blocks_read/docs_read.
|
|
87
|
+
\`_id\` straight into \`where _id == "…"\` (no translation); \`select _id, _path\` is
|
|
88
|
+
simply the envelope (no duplicate columns); an alias \`_id:\`/\`_path:\` on any OTHER
|
|
89
|
+
expression is refused — \`blocks { select _path: _doc._path }\` fails ("projection
|
|
90
|
+
\`_path\` collides with the hit envelope — alias it (\`p: _doc._path\`)"); a row
|
|
91
|
+
surfacing as a value (an empty \`collect { }\`) is the same \`{ _id, _path }\`. Every
|
|
92
|
+
other tool spells a row the same way (\`docs_read\` → \`{ _id, _path, rev, properties,
|
|
93
|
+
_raw }\`, \`blocks_read\` → \`{ _id, _type, _text, _raw, _hash, … }\`, \`docs_list\` → \`{ _id,
|
|
94
|
+
_path, _updated_at, blocks }\`, a revision → \`{ _id, _path, _hash }\`): hydrate by \`_id\`
|
|
95
|
+
via blocks_read/docs_read.
|
|
80
96
|
|
|
81
97
|
## Paths
|
|
82
98
|
|
|
83
99
|
Every path the surface returns is \`/\`-rooted — the form a reference is
|
|
84
100
|
written in (\`[x](/projects/oqx.md)\`, \`before: [/timeline/kickoff.md]\`): a
|
|
85
|
-
hit's \`
|
|
101
|
+
hit's \`_path\`, an edge's \`_dst_path\`, every tool's paths. Every path it
|
|
86
102
|
accepts tolerates both forms (\`/a.md\` or \`a.md\`): tool arguments, \`refs(x)\`,
|
|
87
|
-
\`within(...)\`, and a string LITERAL compared with \`
|
|
88
|
-
through a row handle: \`
|
|
89
|
-
\`.startsWith(...)\` (it is rooted first, so \`
|
|
90
|
-
both match). A property holding a reference compares directly —
|
|
91
|
-
rooted: \`where ^
|
|
92
|
-
\`where
|
|
93
|
-
\`"/" + ...\` glue.
|
|
103
|
+
\`within(...)\`, and a string LITERAL compared with \`_path\`/\`_dst_path\` (on the row,
|
|
104
|
+
or through a row handle: \`_doc._path\`, \`_dst._path\`, \`$it._path\`) by \`==\`/\`!=\` or
|
|
105
|
+
passed to their \`.startsWith(...)\` (it is rooted first, so \`_path == "a.md"\` and
|
|
106
|
+
\`_path == "/a.md"\` both match). A property holding a reference compares directly —
|
|
107
|
+
and is never rooted: \`where ^_path in list(after)\`, \`where customer == ^_path\`,
|
|
108
|
+
\`where path == "/x.md"\` (YOUR authored \`path:\` key — the bare name — compared as
|
|
109
|
+
written) — no \`"/" + ...\` glue.
|
|
94
110
|
|
|
95
111
|
## Targets & field namespaces
|
|
96
112
|
|
|
97
|
-
THE NAME RULE
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
\`
|
|
109
|
-
\`
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
113
|
+
THE NAME RULE — one rule on every record kind: a BARE name is YOURS (an authored
|
|
114
|
+
property: frontmatter + inline on a doc, an attrs key on a block or node), an
|
|
115
|
+
UNDERSCORE-prefixed name is the ENGINE's (however derived: \`_id\`, \`_path\`,
|
|
116
|
+
\`_title\`, \`_updated_at\`, \`_doc\`), and \`$\` belongs to OQX — $it, $key, $depth,
|
|
117
|
+
$stop, $leaf, $frontier, $ordinal are values the LANGUAGE injects into a scope;
|
|
118
|
+
omgbase adds NO $ names (a \`$id\` in a query is an unknown name, absent). Nothing
|
|
119
|
+
bare is reserved: \`title\`, \`tags\`, \`path\`, \`id\`, \`format\`, \`doc\` are your keys
|
|
120
|
+
when you wrote them. Collections stay bare (\`docs { … }\`, \`^docs\`, \`docs count { … }\`,
|
|
121
|
+
\`entries(^$it)\`) — the collection before a body is a root, not a row property — and so do the
|
|
122
|
+
row functions in call position (\`text("x")\`, \`under(…)\`, \`refs(x)\`): a call is
|
|
123
|
+
never an entry read. There is NO self alias (the 4.x \`doc\`/\`block\`/\`section\`):
|
|
124
|
+
\`$it\` is the self, and \`_doc\`/\`_block\`/\`_dst\` reach ANOTHER row. So \`_path\` is the
|
|
125
|
+
engine's /-rooted location and \`path\` your \`path:\` key; \`_tags\` is your tags ∪ the
|
|
126
|
+
body #hashtags and \`tags\` the authored list alone; \`_title\` the computed title (the
|
|
127
|
+
first H1) and \`title\` your \`title:\`; \`_data.<k>\` ≡ \`<k>\` (the merged authored view —
|
|
128
|
+
kept for \`entries(_data)\` and to reach an authored key that itself starts with
|
|
129
|
+
\`_\`), \`_frontmatter.<k>\` / \`_inline.<k>\` one source each. Same value, same name
|
|
130
|
+
everywhere: a hit's \`_id\` is docs_read's \`_id\` and the \`_id\` argument of blocks_read.
|
|
131
|
+
|
|
132
|
+
Engine names per kind (attributes | handles | relations):
|
|
133
|
+
docs _id _path _raw _hash _updated_at _created_at _title _tags _format | _data _frontmatter _inline | _blocks _nodes _out_links _in_links _revisions
|
|
134
|
+
blocks _id _path _doc_id _raw _hash _updated_at _created_at _type _text _attrs _depth _ordinal | _doc | _children _nodes _out_links _section
|
|
135
|
+
nodes _id _path _doc_id _block_id _kind _name _value _attrs | _doc _block | _blocks _subsections _children
|
|
136
|
+
links _id _path _doc_id _block_id _via_id _dst_id _dst_path _uri _field _dst_kind _predicate _provenance _anchor | _doc _dst _block | —
|
|
137
|
+
revisions _id _path _rev _seq _doc_id _commit _ts _origin _actor _reason _hash _is_current _archived | _doc | —
|
|
138
|
+
root _id (^_id, 0^_id) | — | docs blocks nodes links revisions
|
|
139
|
+
Every bare name is yours; \`_attrs.<k>\` on a block or node is the same read as
|
|
140
|
+
the bare attrs key \`<k>\`.
|
|
141
|
+
UNKNOWN NAMES: a bare name that no object of the kind in this repository
|
|
142
|
+
carries is a property read that yields absent — on docs/blocks/nodes the result
|
|
143
|
+
gains \`hints\` (one line per name, the nearest match drawn from your keys and the
|
|
144
|
+
engine names: "no document in this repository carries a property \`title\`; did
|
|
145
|
+
you mean \`_title\`?") and the rows still come back; in a plan/apply/execute
|
|
146
|
+
selector it is refused. On links and revisions (closed: no authored data reaches
|
|
147
|
+
them) a bare name is a filter_invalid error. A \`_x\` that is no engine name of
|
|
148
|
+
the kind is filter_invalid naming the kind's engine names. A property some
|
|
149
|
+
objects carry is known (partial presence is normal).
|
|
126
150
|
|
|
127
151
|
docs:
|
|
128
152
|
- bare identifier = a document PROPERTY (frontmatter + inline \`key:: value\`,
|
|
129
153
|
unioned; nested via dots: meta.owner). YAML/JSON docs
|
|
130
|
-
expose the parsed object's keys the same way.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
154
|
+
expose the parsed object's keys the same way. Nothing is
|
|
155
|
+
reserved: \`title\`, \`tags\`, \`path\`, \`id\` are your keys.
|
|
156
|
+
- source handles = _data.<k> (the merged authored view — the same read as the
|
|
157
|
+
bare key) / _frontmatter.<k> / _inline.<k>; the whole bag
|
|
158
|
+
as a collection via entries(_data) / entries(_frontmatter)
|
|
159
|
+
/ entries(_inline) (see entries()).
|
|
160
|
+
- attributes = _id, _path (/-rooted, see Paths), _updated_at (ISO-8601,
|
|
161
|
+
sorts chronologically), _created_at, _raw (the whole file,
|
|
162
|
+
frontmatter included), _hash (its sha256, 64 hex), _title
|
|
163
|
+
(the computed title: the first H1; \`title\` is the authored
|
|
164
|
+
key), _tags (authored tags ∪ body #hashtags, deduped,
|
|
165
|
+
authored first; \`tags\` is the authored list), _format (the
|
|
166
|
+
doc's format — "markdown"/"yaml"/"json"; present on EVERY
|
|
167
|
+
doc, so has(_format) is always true and \`_format == "yaml"\`
|
|
168
|
+
is the filter).
|
|
169
|
+
- relations = _nodes, _blocks, _out_links / _in_links (a doc's links as
|
|
170
|
+
rows; the documents at their other end are
|
|
171
|
+
_out_links distinct { from _dst } and
|
|
172
|
+
_in_links distinct { from _doc } — there is no
|
|
173
|
+
out / in relation), _revisions (the document's stored
|
|
148
174
|
revisions, oldest first — see revisions below).
|
|
149
175
|
|
|
150
176
|
blocks:
|
|
151
|
-
- bare fields =
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
source — a container's raw holds its children),
|
|
177
|
+
- bare fields = every attrs key FLATTENED onto the row (bare \`checked\`;
|
|
178
|
+
\`_attrs.<k>\` is the same read; absent ⇒ false) — a block's
|
|
179
|
+
authored side.
|
|
180
|
+
- attributes = _id, _path (the owning doc's path), _doc_id (the owning
|
|
181
|
+
doc), _type, _text (normalized), _ordinal (0-based among
|
|
182
|
+
siblings), _depth (0 at the top level), _raw (the block's
|
|
183
|
+
raw source — a container's raw holds its children), _hash
|
|
158
184
|
(its sha256 — the value update/split expect as
|
|
159
|
-
expect.content_hash),
|
|
160
|
-
- reach-through =
|
|
161
|
-
CONTAINING doc (
|
|
162
|
-
read the row's own
|
|
163
|
-
- relations =
|
|
185
|
+
expect.content_hash), _updated_at, _created_at, _attrs.
|
|
186
|
+
- reach-through = _doc.<key> / _doc._path / _doc._id / _doc._format = the
|
|
187
|
+
CONTAINING doc (_doc._id and _doc._path cost nothing — they
|
|
188
|
+
read the row's own _doc_id / joined path, no lookup).
|
|
189
|
+
- relations = _children, _nodes, _out_links, _section (no self alias:
|
|
190
|
+
the 4.x \`block.children\` is \`_children\`).
|
|
164
191
|
NOTE: inside a \`follow\`, $depth/$ordinal are ALWAYS the walk's metadata; a
|
|
165
|
-
block's own nesting is \`
|
|
192
|
+
block's own nesting is \`_depth\` / \`_ordinal\` everywhere.
|
|
166
193
|
|
|
167
194
|
nodes:
|
|
168
|
-
- bare fields =
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
195
|
+
- bare fields = the attrs keys flattened onto the row (checked, level,
|
|
196
|
+
first_ordinal; \`_attrs.<k>\` is the same read).
|
|
197
|
+
- attributes = _id, _path, _doc_id, _block_id (null for a block-less
|
|
198
|
+
node), _kind (md:task / md:link / md:section / yaml:…),
|
|
199
|
+
_name, _value, _attrs.
|
|
200
|
+
- reach-through = _doc.<key> / _doc._id / _doc._path, _block._type /
|
|
201
|
+
_block._text / _block._id / _block._path.
|
|
202
|
+
- relations = _blocks, _children, _subsections (a section node's; the
|
|
203
|
+
4.x \`section.blocks\` is \`_blocks\`).
|
|
175
204
|
|
|
176
205
|
links: (the authored link graph as rows — one per open link: the relation a link
|
|
177
|
-
establishes; the md:link NODE is its occurrence in the text and
|
|
206
|
+
establishes; the md:link NODE is its occurrence in the text and _via_id
|
|
178
207
|
points at it — a frontmatter link has a row and no node)
|
|
179
|
-
-
|
|
180
|
-
|
|
181
|
-
node id: a doc id,
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
("
|
|
186
|
-
("
|
|
208
|
+
- attributes = _id, _path (the SOURCE doc's path), _doc_id (the SOURCE
|
|
209
|
+
doc), _block_id (the source block; null for a frontmatter
|
|
210
|
+
edge), _via_id, _dst_id (the destination node id: a doc id,
|
|
211
|
+
an external node id or phantom:<path>), _dst_path (target
|
|
212
|
+
doc path, /-rooted; NULL when dangling/external), _uri
|
|
213
|
+
(external URL; NULL otherwise), _predicate
|
|
214
|
+
("references"/"embeds"/freeform), _provenance
|
|
215
|
+
("link"/"frontmatter"/"inline_field"/…), _dst_kind
|
|
216
|
+
("document"/"external"/"collection"), _anchor, _field (the
|
|
187
217
|
frontmatter key / inline field it was authored in).
|
|
188
|
-
-
|
|
189
|
-
|
|
190
|
-
|
|
218
|
+
- reach-through = _doc.<key> (the source document), _dst.<key> (the
|
|
219
|
+
destination document; absent when dangling/external),
|
|
220
|
+
_block.<key> (the source block). Closed: links carry no
|
|
221
|
+
authored data, so a bare name is filter_invalid. There is
|
|
222
|
+
no from_commit: a query sees open edges only (history is
|
|
223
|
+
blocks_history/changes_since).
|
|
191
224
|
- text()/semantic() are N/A on links.
|
|
192
225
|
|
|
193
226
|
revisions: (history as rows — one per stored revision of every document of the
|
|
194
227
|
repository, deleted documents included: what docs_history lists, queryable)
|
|
195
|
-
-
|
|
196
|
-
|
|
197
|
-
("observed"/"api"/"import"/
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
was committed under — a move re-paths
|
|
204
|
-
revision, so earlier ones keep the old
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
228
|
+
- attributes = _id (the revision id, r_…), _rev (= _id), _seq (1, 2, … per
|
|
229
|
+
document), _doc_id, _commit, _ts (the commit's time, sorts
|
|
230
|
+
chronologically), _origin ("observed"/"api"/"import"/
|
|
231
|
+
"projection"), _actor, _reason, _hash (the rendered file's
|
|
232
|
+
sha256 — docs_history's _hash), _is_current (true for the
|
|
233
|
+
document's current revision), _archived (true when the
|
|
234
|
+
bodies moved to an archive segment: the row stays,
|
|
235
|
+
docs_read { at } reads through the segment), _path (the
|
|
236
|
+
path the revision was committed under — a move re-paths
|
|
237
|
+
only the current revision, so earlier ones keep the old
|
|
238
|
+
path).
|
|
239
|
+
- reach-through = _doc.<key> reaches the document's CURRENT row; absent for
|
|
240
|
+
a deleted document, so where _doc == null is the revisions
|
|
241
|
+
of deleted documents. Closed: a bare name is filter_invalid.
|
|
242
|
+
- on docs = _revisions is a relation (oldest first): a document with
|
|
243
|
+
more than one version is docs { _revisions count > 1 }.
|
|
209
244
|
- text()/semantic() are N/A on revisions. The body as of a revision is
|
|
210
|
-
docs_read {
|
|
245
|
+
docs_read { _path, at: "<rev>" } (docs_read_at is the same call).
|
|
211
246
|
|
|
212
247
|
## Scoping (ADR-015; the root row since surface 2.0): bare names are LOCAL
|
|
213
248
|
|
|
214
249
|
name the CURRENT row only — never falls through to an enclosing row
|
|
215
250
|
or the repository; an absent field is simply absent.
|
|
216
251
|
^name one scope OUT (the enclosing row's fields/intrinsics/lifts);
|
|
217
|
-
^^name two scopes out. \`^
|
|
252
|
+
^^name two scopes out. \`^_path\` = the enclosing row's path.
|
|
218
253
|
^docs ^nodes ^blocks ^links ^revisions
|
|
219
254
|
THE ROOT ROW (surface 2.0, oqx 0.18): the repository is the root
|
|
220
255
|
scope's row, one scope out from a top-level row, so ^docs there is
|
|
221
256
|
the whole documents scan (uncorrelated until you add a ^ predicate);
|
|
222
257
|
one caret per enclosing block (^^docs from depth two), or the
|
|
223
|
-
absolute 0^docs from ANY depth. ^
|
|
258
|
+
absolute 0^docs from ANY depth. ^_id / 0^_id = the repository id.
|
|
224
259
|
^$it = the root object: ^$it.docs ≡ ^docs, entries(^$it) names the
|
|
225
260
|
five collections. At the root scope itself a bare target is the scan
|
|
226
|
-
(docs
|
|
261
|
+
(docs { … }, docs count { … }, entries(docs)).
|
|
227
262
|
ERRORS: a bare \`docs\` INSIDE a block reads a property of the current
|
|
228
|
-
row, which has none — refused ("did you mean ^docs");
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
263
|
+
row, which has none — refused ("did you mean ^docs"); so is a bare
|
|
264
|
+
\`blocks\`/\`nodes\`/\`revisions\` there (a doc's own relations are
|
|
265
|
+
\`_blocks\`/\`_nodes\`/\`_revisions\`); \`^docs\` AT THE TOP LEVEL reaches
|
|
266
|
+
past the root — refused (write the bare \`docs\`); so is \`$repo\`
|
|
267
|
+
(surface < 2.0; the message names the replacement).
|
|
232
268
|
$it the current item itself (a row, or each ELEMENT when the
|
|
233
|
-
receiver is a list property); ^$it = the enclosing row.
|
|
234
|
-
$value is NOT an intrinsic (since oqx 0.15): it reads a
|
|
269
|
+
receiver is a list property); ^$it = the enclosing row, ^$it._id
|
|
270
|
+
its id. $value is NOT an intrinsic (since oqx 0.15): it reads a
|
|
235
271
|
property literally named $value, absent when there is none.
|
|
236
272
|
$key the row's identity (0.20): a store row's id (doc/block/node/edge), an entries(x)
|
|
237
273
|
entry's key, a follow occurrence's walk identity; project $key: <expr> to rekey the
|
|
@@ -258,7 +294,7 @@ revisions: (history as rows — one per stored revision of every document of th
|
|
|
258
294
|
range-shaped STRING property as an interval: "2026-01-15" in range(window).
|
|
259
295
|
strings x.contains("s") x.startsWith("p") x.endsWith("s") x.matches("^re$")
|
|
260
296
|
(real regex) x.lower() x.upper() size(x). ALL CASE-SENSITIVE —
|
|
261
|
-
fold with .lower():
|
|
297
|
+
fold with .lower(): _title.lower().contains("aurora").
|
|
262
298
|
Infix regex: x ~ "re" is x.matches("re"); x ~* "re" is the
|
|
263
299
|
case-insensitive match; x !~ "re" and x !~* "re" negate them.
|
|
264
300
|
Unanchored unless the pattern anchors; an absent x is false
|
|
@@ -277,7 +313,7 @@ revisions: (history as rows — one per stored revision of every document of th
|
|
|
277
313
|
n.dur_iso() → "PT1H30M". Total: a non-date is absent, so
|
|
278
314
|
\`where date(due) < date("today").minus(dur("30 days"))\` just
|
|
279
315
|
filters. A datetime is never < its own date: compare
|
|
280
|
-
|
|
316
|
+
_updated_at.date() == date("today"), or < date("tomorrow").
|
|
281
317
|
literals "str" 'str' 123 1.5 true false null
|
|
282
318
|
determinism no random; the only clock is date("today"/"now"/…), read once per
|
|
283
319
|
query. Lists/structs are not literals; ternary is not supported.
|
|
@@ -312,8 +348,8 @@ revisions: (history as rows — one per stored revision of every document of th
|
|
|
312
348
|
a source, a receiver or a follow destination:
|
|
313
349
|
follow refs(before), refs(after) walks a timeline both ways.
|
|
314
350
|
The reverse direction needs no function: a document's
|
|
315
|
-
|
|
316
|
-
^docs {
|
|
351
|
+
_path is already the reference form, so
|
|
352
|
+
^docs { ^_path in list(after) } is
|
|
317
353
|
"the documents whose \`after\` names me".
|
|
318
354
|
A HIT IS A STORE ROW: a top-level row that is not a doc/block/node/edge fails
|
|
319
355
|
(filter_invalid). \`follow before\` over a list of paths
|
|
@@ -322,58 +358,64 @@ revisions: (history as rows — one per stored revision of every document of th
|
|
|
322
358
|
## Nested queries & consumers
|
|
323
359
|
|
|
324
360
|
<receiver> collect|exists|none|count|first|single { <block> }
|
|
325
|
-
|
|
326
|
-
|
|
361
|
+
The block may be omitted after a consumer word (OQX 0.26): the omitted block is
|
|
362
|
+
the empty block — \`_nodes exists\`, \`_revisions count > 1\`, \`_blocks first\`,
|
|
363
|
+
\`docs count\` as a whole query; \`x { }\` keeps its braces (the brace is what
|
|
364
|
+
makes a collect). A projection block over a lookup is the lookup's projection:
|
|
365
|
+
\`_blocks[0] { _text }\` ≡ \`_blocks first { _text offset 0 }\`.
|
|
366
|
+
receiver = a relation (_nodes, _blocks, _doc._out_links, _section._blocks, …), a
|
|
367
|
+
reach-through row (_dst, _doc: one row — _dst { _title }), a
|
|
327
368
|
list property (tags), entries(x), or a root collection (^docs; 0^docs at depth ≥ 2).
|
|
328
369
|
block = [select …] [from E] [where …] [frontier …] [follow …] [flatten …] [group by …] [having …] [order by …] [limit N] [offset N]
|
|
329
|
-
— the same fixed order as the
|
|
330
|
-
|
|
331
|
-
projection (\`select\` dropped); a predicate
|
|
332
|
-
A \`from E\` inside a block RE-PROJECTS
|
|
333
|
-
(a typed flatMap):
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
global scan.
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
370
|
+
— the same fixed order as the whole query, whose body is a block
|
|
371
|
+
too (the target is its receiver). A leading name/alias list is the
|
|
372
|
+
projection (\`select\` dropped); a predicate needs \`where\` unless
|
|
373
|
+
it LEADS the block (Sugar). A \`from E\` inside a block RE-PROJECTS
|
|
374
|
+
the rows through a relation (a typed flatMap):
|
|
375
|
+
\`docs { from _nodes where _kind == "md:task" }\` starts at docs,
|
|
376
|
+
projects each through \`_nodes\`, and queries the resulting nodes;
|
|
377
|
+
a scalar relation (\`from _doc\`) contributes one row; a missing or
|
|
378
|
+
non-navigable relation is a loud error, never a global scan. \`from\`
|
|
379
|
+
never names the source — the collection before the body does.
|
|
380
|
+
exists ≥1 row _nodes exists { _kind == "md:task" and not checked }
|
|
381
|
+
none 0 rows _nodes none { _kind == "md:task" and not checked } ("every task done")
|
|
382
|
+
count a number _nodes count { _kind == "md:task" } >= 2
|
|
341
383
|
collect rows (default) — in select for nested results; in where with a lift
|
|
342
384
|
first / single zero-or-one (single errors on >1) — lookups in select
|
|
343
|
-
distinct on any consumer: dedup by projected value —
|
|
385
|
+
distinct on any consumer: dedup by projected value — _nodes count distinct { select _kind }
|
|
344
386
|
limit/offset bound the block's rows AFTER where/order/distinct, BEFORE the
|
|
345
|
-
consumer reduces:
|
|
387
|
+
consumer reduces: _nodes exists { offset 1 } = at least two;
|
|
346
388
|
first { … offset 1 } = the second.
|
|
347
389
|
lift \`^name:\` in a where-collect binds values outward:
|
|
348
|
-
select
|
|
349
|
-
joins ^<t> exists {
|
|
350
|
-
^nodes single {
|
|
390
|
+
docs { select _path, open where _nodes { ^open: _value where _kind == "md:task" and not checked } }
|
|
391
|
+
joins ^<t> exists { slug == ^ref } semi-join; not … exists anti-join;
|
|
392
|
+
^nodes single { _kind == "person" and _attrs.id == ^owner_id } lookup;
|
|
351
393
|
x in ^keys membership over a lifted set.
|
|
352
394
|
|
|
353
395
|
## select — projection
|
|
354
396
|
|
|
355
|
-
select a, alias: expr, nested:
|
|
356
|
-
bare key →
|
|
357
|
-
|
|
397
|
+
select a, alias: expr, nested: _nodes collect { … }
|
|
398
|
+
bare key → your property (scalar-authored → scalar; list → array); _name →
|
|
399
|
+
the engine's field (_id, _path, _title, …); _data.<k> is the bare key's read;
|
|
358
400
|
a call/arithmetic needs an alias (n: size(tags)) unless in values mode;
|
|
359
|
-
an output key of
|
|
360
|
-
|
|
361
|
-
distinct select distinct
|
|
401
|
+
an output key of _id/_path other than the attribute itself needs an alias.
|
|
402
|
+
_raw whole file bytes (docs) — docs_read is cheaper for one doc.
|
|
403
|
+
distinct select distinct _type (dedup hits by projected value)
|
|
362
404
|
values select <ONE item> values → bare values instead of records:
|
|
363
|
-
select distinct type values
|
|
364
|
-
select
|
|
365
|
-
tags: tags
|
|
366
|
-
h:
|
|
405
|
+
docs { select distinct type values } → values: ["hub","lab-note",…]
|
|
406
|
+
docs { select _path values } → values: ["/index.md", …]
|
|
407
|
+
tags: tags { $it values } → a plain array
|
|
408
|
+
h: _nodes first { _name values where _kind == "md:section" order by first_ordinal }
|
|
367
409
|
entries(x) a record as a collection ($key / $it per entry), key order:
|
|
368
|
-
select fm: entries(
|
|
369
|
-
where entries(
|
|
370
|
-
entries(
|
|
410
|
+
select fm: entries(_data) { k: $key, v: $it }
|
|
411
|
+
where entries(_frontmatter) exists { $key == "era" and $it > 1600 }
|
|
412
|
+
entries(_inline), entries(_attrs), a nested map, or a list (index keys)
|
|
371
413
|
$semantic_score is not a field — project semantic("…") under an alias instead.
|
|
372
414
|
|
|
373
415
|
## flatten (OQX 0.25)
|
|
374
416
|
|
|
375
417
|
flatten <expr> [{ … }] [as <name>] after follow, before group by; may repeat
|
|
376
|
-
Splits one value into rows. Every hit is still the PARENT row — its
|
|
418
|
+
Splits one value into rows. Every hit is still the PARENT row — its _id/_path
|
|
377
419
|
repeat per element, as Dataview's TABLE … FLATTEN — with <name> bound beside
|
|
378
420
|
its fields at the same depth: an array → one row per element (an empty list
|
|
379
421
|
yields none), a missing field → one row with the name absent (never dropped),
|
|
@@ -383,44 +425,44 @@ revisions: (history as rows — one per stored revision of every document of th
|
|
|
383
425
|
projection, order by, limit, distinct), applied first. Later clauses read the
|
|
384
426
|
name — a later flatten (flatten jobs as j flatten j.tags as t), group by,
|
|
385
427
|
having, select, order by; where runs before it. A flattened query pages by
|
|
386
|
-
position (cursor null); as
|
|
387
|
-
select
|
|
388
|
-
select
|
|
389
|
-
select tags, n: count()
|
|
428
|
+
position (cursor null); as _id / as _path is refused (the envelope reads them).
|
|
429
|
+
docs { select _path, tags where type == "process" flatten tags order by _path, tags }
|
|
430
|
+
docs { select _path, t: b._text flatten _blocks { _type == "task" and not checked } as b }
|
|
431
|
+
docs { select tags, n: count() flatten tags group by tags order by count() desc }
|
|
390
432
|
|
|
391
433
|
## order by, limit/offset, pagination
|
|
392
434
|
|
|
393
|
-
order by era desc,
|
|
435
|
+
order by era desc, _path asc absent values sort LAST (asc); ties → (_path, _id)
|
|
394
436
|
limit N / offset N define the result SET (integer literals);
|
|
395
437
|
the tool's limit/cursor then page WITHIN it
|
|
396
|
-
cursor opaque keyset on (
|
|
438
|
+
cursor opaque keyset on (_path, _id); a custom order by
|
|
397
439
|
disables it (you get the top page + truncated)
|
|
398
440
|
|
|
399
441
|
## Graph traversal (follow) and links
|
|
400
442
|
|
|
401
443
|
The citation graph is the LINK ROWS walked through a destination block:
|
|
402
|
-
|
|
403
|
-
link — it then contributes no successor),
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
follow
|
|
407
|
-
follow
|
|
408
|
-
follow
|
|
444
|
+
_dst is a link's destination document (absent for an external or dangling
|
|
445
|
+
link — it then contributes no successor), _doc its source.
|
|
446
|
+
docs { where <seed> follow _out_links { from _dst } } docs the seed links TO
|
|
447
|
+
docs { where <seed> follow _in_links { from _doc } } backlinks
|
|
448
|
+
follow _out_links { from _dst depth 3 } bound (1..8, default 8) — a clause of the block
|
|
449
|
+
follow _out_links { from _dst where layer == "canon" } keep only matching successors
|
|
450
|
+
follow _in_links { from _doc where ^_path in list(before) } ^ = the row being expanded
|
|
409
451
|
(^^ = the walk's enclosing scope)
|
|
410
|
-
follow
|
|
411
|
-
follow
|
|
452
|
+
follow _out_links { from _dst where _predicate == "depends_on" } one predicate only
|
|
453
|
+
follow _out_links { from _dst }, _in_links { from _doc }
|
|
412
454
|
several destinations: their union
|
|
413
455
|
(one step dedups by identity)
|
|
414
|
-
follow ^docs {
|
|
456
|
+
follow ^docs { _out_links exists { _dst_path == ^^_path } }
|
|
415
457
|
a destination block over the root's
|
|
416
458
|
documents, re-read per frontier row
|
|
417
459
|
(^ = that row) = the backlinks walk
|
|
418
|
-
follow
|
|
419
|
-
|
|
460
|
+
follow _out_links { from _dst frontier type == "practitioner" } cut, keeping the frontier row
|
|
461
|
+
docs { type == "process" frontier !verified follow _out_links { from _dst } }
|
|
420
462
|
the BODY's frontier applies to the seeds:
|
|
421
463
|
an unverified seed is kept, not expanded
|
|
422
|
-
follow distinct
|
|
423
|
-
follow refs(before), ^docs {
|
|
464
|
+
follow distinct _out_links { from _dst } / … { $key: group from _dst } identity control ($key)
|
|
465
|
+
follow refs(before), ^docs { ^_path in list(after) } forward over \`before\`, backward
|
|
424
466
|
over every doc whose \`after\` names me
|
|
425
467
|
Per-occurrence metadata: $depth (seed = 1), $stop (interior|leaf|frontier|
|
|
426
468
|
depth|cycle), $leaf/$frontier, $ordinal (a deterministic 1..N rank over the
|
|
@@ -428,14 +470,15 @@ revisions: (history as rows — one per stored revision of every document of th
|
|
|
428
470
|
\`where $ordinal <= N\` for a deterministic budget cut), $key — usable in
|
|
429
471
|
select/order by and the top-level post-walk where (a where conjunct that
|
|
430
472
|
reads one filters the walk's RESULT; the other conjuncts remain the seed
|
|
431
|
-
predicate), NOT metadata of the candidates a destination block's where reads
|
|
432
|
-
|
|
473
|
+
predicate), NOT metadata of the candidates a destination block's where reads;
|
|
474
|
+
a consumer directive may carry a follow of its own (its block yields the
|
|
475
|
+
walk: \`_children count { follow _children } > 5\`). Cyclic graphs are safe: a revisited
|
|
433
476
|
node is admitted once as a \`$stop == "cycle"\` occurrence and never
|
|
434
477
|
re-expanded. The default keeps one occurrence per distinct walk path;
|
|
435
478
|
\`follow distinct\` dedups reached rows by identity.
|
|
436
479
|
Destination blocks (OQX 0.20): a brace after ANY destination opens that
|
|
437
|
-
destination's block —
|
|
438
|
-
|
|
480
|
+
destination's block — _children { depth 2 }, refs(x) { depth 2 }, _out_links
|
|
481
|
+
{ from _dst depth 2 }, ^docs { ^_path in list(after) } — with the
|
|
439
482
|
0.17 body rules (where-first, projections) and two clauses of its own after
|
|
440
483
|
\`where\`: frontier <pred> (occurrences reached through this destination that
|
|
441
484
|
match are frontiers; ^ there is the walk's enclosing scope) and depth <n>
|
|
@@ -446,55 +489,155 @@ revisions: (history as rows — one per stored revision of every document of th
|
|
|
446
489
|
expanded; each destination's frontier is its own. There is no options block;
|
|
447
490
|
the old \`by\` is $key. A bare follow ^docs (no brace, no consumer) is an
|
|
448
491
|
error naming both block spellings.
|
|
449
|
-
Type-preserving relations: block
|
|
450
|
-
the citation graph is
|
|
451
|
-
|
|
452
|
-
select d:
|
|
453
|
-
|
|
454
|
-
select b:
|
|
455
|
-
select
|
|
456
|
-
|
|
492
|
+
Type-preserving relations: a block's _children; a section node's _children and
|
|
493
|
+
_subsections; the citation graph is _out_links { from _dst } / _in_links { from _doc }.
|
|
494
|
+
links { _dst_path == "/notes/x.md" } inbound links to a doc, as rows
|
|
495
|
+
links { select d: _dst._title, k: _dst_kind } the destination document's title
|
|
496
|
+
docs { _out_links exists { _dst.layer == "draft" } } links into drafts
|
|
497
|
+
docs { select b: _in_links distinct { from _doc } } backlinks as { _id, _path }
|
|
498
|
+
links { select _doc_id, _dst_path where _predicate == "depends_on" }
|
|
499
|
+
docs { not _in_links exists } orphans (nothing links in)
|
|
457
500
|
(The \`graph\` tool is a convenience wrapper that compiles to a follow query.)
|
|
458
501
|
|
|
459
502
|
## semantic grain
|
|
460
503
|
|
|
461
|
-
|
|
504
|
+
docs { … } ranks whole DOCUMENTS (one hit per file); blocks { … } ranks PASSAGES.
|
|
462
505
|
text()/where prune the candidate set; only order by reweights.
|
|
463
506
|
|
|
464
507
|
## Examples
|
|
465
508
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
select layer, tags
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
select fm: entries(
|
|
476
|
-
select owner_id, owner: ^nodes single {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
select
|
|
480
|
-
select
|
|
481
|
-
select
|
|
482
|
-
select
|
|
509
|
+
docs { layer == "working" }
|
|
510
|
+
docs { _path.startsWith("/guides/") and _updated_at >= "2026-08-01" }
|
|
511
|
+
docs { select layer, tags where "pricing" in list(tags) }
|
|
512
|
+
docs { _inline.owner == "alice" }
|
|
513
|
+
docs { _title.lower().contains("q3 plan") }
|
|
514
|
+
docs { _format == "yaml" }
|
|
515
|
+
docs { type == "practitioner" order by era desc limit 2 }
|
|
516
|
+
docs { _nodes none { _kind == "md:task" and not checked } }
|
|
517
|
+
docs { tags exists { $it == "tria-prima" } }
|
|
518
|
+
docs { select fm: entries(_data) { k: $key, v: $it } where _path == "/x.md" }
|
|
519
|
+
docs { select owner_id, owner: ^nodes single { _kind == "person" and _attrs.id == ^owner_id } }
|
|
520
|
+
blocks { _type == "task" and not checked and under_heading("Launch") and _doc.layer == "working" }
|
|
521
|
+
blocks { _type == "paragraph" and has_edge("references", "d_92aaaaa") }
|
|
522
|
+
docs { select _path, $depth where _path == "/timeline/review.md" follow refs(before), refs(after) }
|
|
523
|
+
docs { select _path, next: ^docs { _path where ^_path in list(before) } where type == "milestone" }
|
|
524
|
+
docs { select _path, n: size(^docs), repo: ^_id limit 1 }
|
|
525
|
+
docs { select path, title where has(path) }
|
|
483
526
|
docs { type == "substance" and not verified }
|
|
484
|
-
select subject, process: ^docs[slug == ^subject]!.
|
|
485
|
-
select
|
|
486
|
-
select
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
docs count {
|
|
490
|
-
select
|
|
491
|
-
|
|
492
|
-
select p:
|
|
493
|
-
select layer, n: count(), titles: collect(
|
|
494
|
-
select
|
|
495
|
-
|
|
496
|
-
Common mistake: \`$path\`/\`$id\`
|
|
497
|
-
\`
|
|
498
|
-
\`
|
|
527
|
+
docs { select subject, process: ^docs[slug == ^subject]!._path where type == "lab-note" }
|
|
528
|
+
docs { select _path, back: _nodes first { select p: 0^docs { _path where ^^_path in list(after) } values } where type == "milestone" }
|
|
529
|
+
docs { select _path, prior: refs(before) { _path, when } where type == "milestone" }
|
|
530
|
+
blocks { order by semantic("identity preservation across edits") desc limit 10 }
|
|
531
|
+
links { select _uri where _dst_kind == "external" }
|
|
532
|
+
docs count { layer == "canon" }
|
|
533
|
+
revisions { select _seq, _origin, _ts, _hash where _path == "/processes/calcination.md" order by _seq desc }
|
|
534
|
+
docs { _revisions count { _origin == "api" } > 0 }
|
|
535
|
+
revisions { select p: _path, d: _doc._path where _doc == null }
|
|
536
|
+
docs { select layer, n: count(), titles: collect(_title), open: rows count { _nodes exists { _kind == "md:task" and not checked } } group by layer having count() > 1 order by count() desc }
|
|
537
|
+
docs { select _path, tag: tags where "process" in tags flatten tags order by _path, tags }
|
|
538
|
+
|
|
539
|
+
Common mistake: \`$path\`/\`$id\` — omgbase has no $ names; the engine's location and
|
|
540
|
+
identity are \`_path\`/\`_id\`. Another: the 4.x bare \`path\`/\`id\`/\`title\`/\`tags\` — a
|
|
541
|
+
bare \`path\` is now YOUR \`path:\` key (absent when you wrote none, with a hint
|
|
542
|
+
pointing at \`_path\`); the engine's are \`_path\`/\`_id\`/\`_title\`/\`_tags\`; \`select
|
|
543
|
+
data.path\` is gone — write \`path\`. Another: \`where tags == "x"\` on a list-valued
|
|
544
|
+
key is false — write \`"x" in tags\`. Another: \`select _path: _doc._path\` — alias it
|
|
545
|
+
(\`p: _doc._path\`).
|
|
546
|
+
`;
|
|
547
|
+
/**
|
|
548
|
+
* The manual's chapters (spec/surface §4 `query_syntax`, 5.1): one entry per
|
|
549
|
+
* `## ` heading of `QUERY_SYNTAX`, in order — the `section` argument's names.
|
|
550
|
+
* `querySyntaxSection(slug)` is the text from that heading up to the next one.
|
|
551
|
+
* The Rust twin pins the same table (its test checks every pair against this
|
|
552
|
+
* source).
|
|
553
|
+
*/
|
|
554
|
+
export const QUERY_SYNTAX_SECTIONS = [
|
|
555
|
+
{ slug: "sugar", heading: "Sugar (OQX 0.17)" },
|
|
556
|
+
{ slug: "paths", heading: "Paths" },
|
|
557
|
+
{ slug: "targets", heading: "Targets & field namespaces" },
|
|
558
|
+
{ slug: "scoping", heading: "Scoping (ADR-015; the root row since surface 2.0): bare names are LOCAL" },
|
|
559
|
+
{ slug: "scalars", heading: "Scalar semantics (@omgbase/oqx)" },
|
|
560
|
+
{ slug: "predicates", heading: "Domain predicates (omgbase)" },
|
|
561
|
+
{ slug: "nested", heading: "Nested queries & consumers" },
|
|
562
|
+
{ slug: "select", heading: "select — projection" },
|
|
563
|
+
{ slug: "flatten", heading: "flatten (OQX 0.25)" },
|
|
564
|
+
{ slug: "order", heading: "order by, limit/offset, pagination" },
|
|
565
|
+
{ slug: "follow", heading: "Graph traversal (follow) and links" },
|
|
566
|
+
{ slug: "semantic", heading: "semantic grain" },
|
|
567
|
+
{ slug: "examples", heading: "Examples" },
|
|
568
|
+
];
|
|
569
|
+
/** The `section` names a caller may pass: every slug, then `all` (the whole manual). */
|
|
570
|
+
export const QUERY_SYNTAX_SECTION_NAMES = [...QUERY_SYNTAX_SECTIONS.map((s) => s.slug), "all"];
|
|
571
|
+
/**
|
|
572
|
+
* One chapter of the manual: the lines from `## <heading>` up to (not
|
|
573
|
+
* including) the next `## ` heading or the end, trailing blank lines dropped,
|
|
574
|
+
* one trailing newline. `"all"` is the whole manual; an unknown slug is `null`.
|
|
575
|
+
*/
|
|
576
|
+
export function querySyntaxSection(slug) {
|
|
577
|
+
if (slug === "all")
|
|
578
|
+
return QUERY_SYNTAX;
|
|
579
|
+
const entry = QUERY_SYNTAX_SECTIONS.find((s) => s.slug === slug);
|
|
580
|
+
if (!entry)
|
|
581
|
+
return null;
|
|
582
|
+
const lines = QUERY_SYNTAX.split("\n");
|
|
583
|
+
const start = lines.indexOf(`## ${entry.heading}`);
|
|
584
|
+
if (start < 0)
|
|
585
|
+
return null;
|
|
586
|
+
let end = start + 1;
|
|
587
|
+
while (end < lines.length && !lines[end].startsWith("## "))
|
|
588
|
+
end++;
|
|
589
|
+
while (end > start + 1 && lines[end - 1].trim() === "")
|
|
590
|
+
end--;
|
|
591
|
+
return lines.slice(start, end).join("\n") + "\n";
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* The card `query_syntax {}` returns (5.1): the grammar skeleton, the names
|
|
595
|
+
* rule and the engine names per kind, the handles, the consumers, follow in
|
|
596
|
+
* one line, the examples agents reach for first, and where the chapters are.
|
|
597
|
+
* Every indented example parses (reference.test.ts extracts them exactly as it
|
|
598
|
+
* extracts the manual's). Keep every line earning its place: this is the most
|
|
599
|
+
* read text on the surface.
|
|
600
|
+
*/
|
|
601
|
+
export const QUERY_SYNTAX_CARD = `# query — OQX card. Chapters: query_syntax { section: "<name>" }; the manual: { section: "all" }
|
|
602
|
+
|
|
603
|
+
One OQX string; limit/cursor page the hits; bare: true drops the { _id, _path } envelope.
|
|
604
|
+
<target> { [select <items>] [where <pred>] [follow <dest> { … }] [order by <expr> [desc]] [limit N] [offset N] }
|
|
605
|
+
<target> count|exists|none|first|single [{ <block> }] a scalar or one row; no block = the empty block (docs count)
|
|
606
|
+
The collection (docs blocks nodes links revisions) heads the body; nothing follows its closing brace.
|
|
607
|
+
CLAUSE ORDER IS FIXED: select, from, where, frontier, follow, flatten, group by, having, order by, limit, offset.
|
|
608
|
+
and / or / not. Comparisons strict and typed; null == a missing field. A list key: "x" in tags, never tags == "x".
|
|
609
|
+
Strings .contains("s") .startsWith("p") .lower(); x ~* "re" (ci regex), x !~ "re". Dates date("today"),
|
|
610
|
+
date(due) < date("today").minus(dur("30 days")), _updated_at >= "2026-08-01".
|
|
611
|
+
|
|
612
|
+
NAMES: a BARE name is YOURS (frontmatter, inline key:: value, a block's attrs); an UNDERSCORE name is the ENGINE's;
|
|
613
|
+
$ is OQX's ($it $key $depth $stop $ordinal). title tags path id are your keys; _title _tags _path _id the engine's.
|
|
614
|
+
docs _id _path _raw _hash _updated_at _created_at _title _tags _format | _data _frontmatter _inline | _blocks _nodes _out_links _in_links _revisions
|
|
615
|
+
blocks _id _path _doc_id _raw _hash _type _text _attrs _depth _ordinal | _doc | _children _nodes _out_links _section
|
|
616
|
+
nodes _id _path _doc_id _block_id _kind _name _value _attrs | _doc _block | _blocks _subsections _children
|
|
617
|
+
links _id _path _doc_id _block_id _via_id _dst_id _dst_path _uri _dst_kind _predicate _provenance _anchor _field | _doc _dst _block
|
|
618
|
+
revisions _id _path _rev _seq _doc_id _commit _ts _origin _actor _reason _hash _is_current _archived | _doc
|
|
619
|
+
Handles _data.<k> ≡ <k>, _frontmatter.<k> / _inline.<k>; entries(_data) the bag as rows ($key, $it). Reach-through
|
|
620
|
+
_doc.<k> the owning document, _dst.<k> a link's target. Scoping: a bare name is the CURRENT row; ^name one scope
|
|
621
|
+
out; ^docs ^blocks ^nodes ^links the repository from a row. An unknown bare name is absent plus a hints line.
|
|
622
|
+
|
|
623
|
+
NESTED: <receiver> collect|exists|none|count|first|single { [select …] [where …] [order by …] [limit N] } over a
|
|
624
|
+
relation, a list key, entries(x) or ^docs; no consumer = collect, a leading predicate = where: docs { type == "x" }.
|
|
625
|
+
FOLLOW: docs { where <seed> follow _out_links { from _dst depth 3 } } links out; _in_links { from _doc }
|
|
626
|
+
backlinks; refs(before) a property's documents; $depth per row. SEARCH: text("words") prunes; order by semantic("…") desc.
|
|
627
|
+
|
|
628
|
+
docs { layer == "working" }
|
|
629
|
+
docs { select _title, stage where "process" in tags order by _path limit 5 }
|
|
630
|
+
blocks { select _type, _text where _path == "/processes/calcination.md" }
|
|
631
|
+
nodes { select _path, _value where _kind == "md:task" and not checked }
|
|
632
|
+
links { select _path, _block_id where _dst_path == "/processes/calcination.md" }
|
|
633
|
+
docs { _title.lower().contains("salt") and _updated_at >= "2026-01-01" }
|
|
634
|
+
docs { _nodes exists { _kind == "md:task" and not checked } }
|
|
635
|
+
docs { select _path, $depth where _path == "/timeline/review.md" follow refs(before), refs(after) }
|
|
636
|
+
docs count { layer == "canon" }
|
|
637
|
+
revisions { select _seq, _ts, _hash where _path == "/processes/calcination.md" order by _seq desc }
|
|
638
|
+
docs { select tags, n: count() flatten tags group by tags order by count() desc }
|
|
639
|
+
|
|
640
|
+
Every error is filter_invalid naming what to change; when the engine can correct the call, data.fix IS the call.
|
|
641
|
+
Chapters: sugar paths targets scoping scalars predicates nested select flatten order follow semantic examples; all.
|
|
499
642
|
`;
|
|
500
643
|
//# sourceMappingURL=reference.js.map
|