1pub const QUERY_SYNTAX: &str = r#"# query — OQX syntax reference
6
7The `query` tool takes ONE plain OQX string (+ optional `limit`/`cursor`). There
8is no {from, filter, order} envelope: every concern is a clause of the string.
9
10 [select <items>] from <target> [where <pred>] [follow <dest>, … [{ … }]]
11 [order by <expr> [asc|desc], …] [limit N] [offset N]
12 <target> count|exists|none|first|single { <block> } (scalar/one-row form: a bare target at the root scope)
13
14CLAUSE ORDER IS FIXED: select, from, where, follow, order by, limit, offset —
15each at most once; an out-of-order clause is a parse error naming the order.
16Only `select` may drop its keyword, and only as the first clause
17(`$path, era from docs where …`). A top-level predicate needs `where`, because
18`where` follows `from` (`era > 1600 from docs` is the clause-order error); a
19block may LEAD with a predicate (see Sugar), but a predicate after a projection
20still needs the keyword (`{ name where kind == "md:task" }`, not
21`{ name, kind == "md:task" }`); `from docs count` is an error (write
22`docs count { … }`). `where` and later `select` items may use the same body's
23`select` aliases (`select $path, old: era < 1000 from docs where old`; an alias
24shadows a same-named field there). `and` / `or` / `not` are the connectives;
25the symbols `&&` / `||` / `!` are accepted synonyms (the words are canonical and
26what `print` writes).
27
28## Sugar (OQX 0.17)
29
30Every form here is shorthand for an explicit directive (the AST is the explicit form):
31 nodes { kind == "md:task" } ≡ nodes collect { where kind == "md:task" } — a receiver block with
32 no consumer is collect; a block whose LEADING expression is a predicate
33 (comparison, call, !/is/not, in, literal, (…), a consumer test — anything
34 but a bare name, a dotted path or a ^lift) is where-first. Bare names still
35 project: nodes { name } ≡ nodes collect { name }; filter a bare field with
36 nodes { is checked }. After follow only over an outer-reference receiver (0.19):
37 follow ^docs { where … } is a destination block (collect); follow children { depth 2 }
38 and follow refs(x) { depth 2 } are the options block.
39 refs(x)[0] ≡ refs(x) first { offset 0 } positional (integer literal or ${binding});
40 out of range ⇒ absent; refs(x)[0].$path navigates the row
41 ^docs[$path == ^company] ≡ ^docs first { where $path == ^company } first match or absent
42 ^docs[$path == ^company]! ≡ … single { … } required: exactly one, else filter_invalid
43 title! required: title, or an error naming the expression (and the row's $id)
44 — never a filter, never a coercion (0!, ""! are values); tightest: a!.b vs a.b!
45 is x / not x truthiness of x, and its negation (prefix; `not` ≡ `!`)
46 x is y / x is not y identity (a row's id, else structural) — compares rows, which == does not;
47 x is null = absent; for scalars is ≡ ==; comparison precedence, no chaining
48 a and b / a or b the connectives (≡ && / ||: same precedence, short-circuit, value: title or $path coalesces)
49 boss: ^docs[$path == ^manager], bossName: boss.$title a select item may use the items to its LEFT
50 Reserved words (never a bare field name): from where select is not and or, true false null.
51
52Results are LEAN hits — {id, path} + whatever `select` projects (or `values`,
53`count`, `exists`, `none`). Hydrate full content by id via nodes_get/docs_read.
54
55## Paths
56
57Every path the surface returns is `/`-rooted — the form a reference is
58written in (`[x](/projects/oqx.md)`, `before: [/timeline/kickoff.md]`): a
59hit's `path`, `$path`, `$dst_path`, every tool's paths. Every path it
60accepts tolerates both forms (`/a.md` or `a.md`): tool arguments, `refs(x)`,
61`within(...)`, and a string LITERAL compared with `$path`/`$dst_path` by
62`==`/`!=` or passed to their `.startsWith(...)` (it is rooted first, so
63`$path == "a.md"` and `$path == "/a.md"` both match). A property holding a
64reference compares directly: `where ^$path in list(after)`,
65`where customer == ^$path` — no `"/" + ...` glue.
66
67## Targets & field namespaces
68
69`$`-prefixed names are engine intrinsics; BARE identifiers are your content.
70
71docs: bare identifier = a document PROPERTY (frontmatter + inline, nested via
72 dots); frontmatter.<k> / inline.<k> force a source; entries(frontmatter)
73 is the whole bag; $title, $tags, format; $id, $path, $updated_at, $body,
74 $content_hash; relations nodes, blocks, doc.out / doc.in,
75 doc.out_edges / doc.in_edges. A bare id/path/updated_at/content_hash/
76 body is rejected ("did you mean $x?").
77blocks: type, text, attrs keys flattened (`checked`); $id, $doc, $path,
78 $ordinal, $depth, $updated_at, $body, $content_hash; doc.<key>;
79 block.children, block.nodes, block.out_edges, section.
80nodes: kind, name, value, attrs keys flattened; $id, $doc_id, $block_id,
81 $path; doc.<key>, block.<field>; section.blocks, section.children,
82 section.subsections.
83edges: predicate, provenance, dst_kind, anchor, src_field; $id, $src, $dst,
84 $dst_path, $dst_uri, $src_block, $via, $from_commit; $path and
85 doc.<key> reach the SOURCE document.
86
87## Functions
88
89text("terms") — FTS prune (docs/blocks/nodes). semantic("phrase") — cosine
90score (docs/blocks; needs a provider). Blocks only: under(id),
91under_heading(s), within(doc id | path glob), under_kind(type[, name]),
92yaml_path(p), json_pointer(p), has_anchor(), child_count(), parent_type().
93has_edge(pred[, dst]) on docs and blocks. Free: list, size, has, entries,
94range, refs. Methods: contains, startsWith, endsWith, matches, size, lower,
95upper.
96
97refs(x) — the live documents a property's document references name: x is a
98string, a list or absent; each "/a/b.md", "a/b.md" or "d_…" element resolves
99to that doc's row; dangling and non-string elements are dropped. Yields docs
100rows, so it is a source, a receiver or a follow destination:
101`follow refs(before), refs(after)` walks a timeline both ways. The reverse
102direction needs no function: a document's $path is already the reference
103form, so `^docs { where ^$path in list(after) }` is "the
104documents whose `after` names me". A HIT IS A STORE ROW: a top-level row that is not
105a doc/block/node/edge fails (filter_invalid) — `follow before` over a list of
106paths reaches the STRINGS; write `follow refs(before)`.
107
108## Directives
109
110<receiver> exists|none|count|collect|first|single { <block> } — nested,
111correlated to the current row; `^name` reads one scope out. THE ROOT ROW
112(surface 2.0, oqx 0.18): the repository is the root scope's row, so from a
113top-level row `^docs` / `^nodes` / `^blocks` / `^edges` are the whole
114collections (unbounded until a `^` predicate correlates them) — one caret per
115enclosing block (`^^docs` from depth two), or the absolute `0^docs` from any
116depth; `^$id` / `0^$id` is the repository id; `^$it` the root object
117(`^$it.docs` ≡ `^docs`, `entries(^$it)` names the four collections). At the
118root scope a bare target is the scan (`docs count { … }`, `from docs`). A
119bare `docs` INSIDE a block reads a property of the current row and is refused
120("did you mean `^docs`"); `^docs` AT THE TOP LEVEL reaches past the root and is
121refused (write the bare `docs`); so is `$repo` (surface < 2.0), with the
122replacement named. `nodes`/`blocks` on a doc row are that doc's relations, not the root.
123`distinct` dedups by projection;
124`values` returns bare values; `limit`/`offset` bound a block before its
125consumer. `follow <dest>, … { where … frontier … depth N by … }` recurses
126over type-preserving destinations ($depth, $stop, $leaf, $frontier,
127$ordinal): each <dest> is a relation of the current row or a
128`<recv> collect|first|single [distinct] { … }` block re-evaluated per frontier
129row; one step's successors are unioned by identity. Inside the follow-local
130`where` and inside a destination block `^` is the row being expanded (`^^`
131the walk's enclosing scope): `follow doc.in { where ^$path in list(before) }`,
132`follow doc.out, ^docs collect { where ^$path in list(after) }`.
133Outer-reference destinations (OQX 0.19): a destination whose receiver is headed
134by an outer reference (^docs, ^^docs, 0^docs, ^$it.docs, with any member chain)
135can never be a relation of the current row, so a brace right after it is a
136destination block with the implicit collect — follow refs(before), ^docs { where
137^$path in list(after) } walks `before` forward and, backward, every document
138whose `after` names the frontier row (the 0.17 body rules apply: where-first,
139projections). After children / doc.out / refs(x) / a binding a brace is still
140the options block (an error when that destination is not the last); an options
141block may follow a trailing block: follow ^docs { where … } { depth 2 }. A bare
142follow ^docs (no brace, no consumer) is an error naming both block spellings.
143A custom `order by` disables the keyset cursor.
144"#;