feat(client): read-model surface — open/sync + anchorless union sparql_query
Proven against the real broker (probe): opening docs then a single anchorless
`sparql_query` with a `GRAPH ?g { ... }` body reads the LOCAL UNION of all synced
named graphs — the fast, hang-free replacement for the reactive-ORM per-document
fan-out (which aborted on any unsynced/fresh repo → 75s never-fires). New
`read-model.ts` (readUnion) exposes this; there is no reactive union query, so
listing is one-shot and consumers re-query on change. docs/read-model.md refined
with the probe finding (an explicit `GRAPH ?g` body iterates all named graphs
regardless of the anchor; the anchor only bounds the default graph). 93 tests
pass; tsc rc=0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -112,3 +112,32 @@ The one experiment that pins down union vs anchor, to run against a real broker:
|
||||
If (3) returns both and (4) returns only A, the union read model above holds as
|
||||
implemented in `resolve_target_for_sparql` /
|
||||
`set_default_graph_as_union`.
|
||||
|
||||
### Verified against the real broker (T03.k)
|
||||
|
||||
Step (3) — **the load-bearing one** — is CONFIRMED: an anchorless
|
||||
`SELECT … WHERE { GRAPH ?g { ?s ?p ?o } }` returns triples from BOTH docs A and B
|
||||
(the local union of the opened graphs). That is the entire premise the listing
|
||||
path relies on.
|
||||
|
||||
Step (4) has a nuance worth recording: with an **explicit `GRAPH ?g { … }` body**,
|
||||
passing `anchor = A` did **not** restrict the result to A (B still appeared). The
|
||||
reason: the anchor sets the query's **default graph**, but a `GRAPH ?g` pattern
|
||||
iterates over the **named graphs** regardless of the default graph — so an
|
||||
explicit `GRAPH ?g` body spans every opened graph independently of the anchor.
|
||||
The anchor's "one repo" restriction is observable only for a body that reads the
|
||||
**default graph** (no `GRAPH` wrapper). The read model never needs the anchored
|
||||
form for listing — it uses the anchorless `GRAPH ?g` union — so this does not
|
||||
affect it. (The per-doc **open** step in `read-model.ts` uses an anchored `ASK`
|
||||
purely for its side effect of opening the repo, not to restrict a read.)
|
||||
|
||||
## Implementation — `read-model.ts`
|
||||
|
||||
`readModel.readUnion(docs)` implements this: (1) open/sync each doc via a per-doc
|
||||
anchored `ASK` (tolerant — a doc that can't open is skipped, never aborting the
|
||||
listing like the ORM fan-out would); (2) run ONE anchorless
|
||||
`SELECT ?g ?s ?p ?o WHERE { GRAPH ?g { ?s ?p ?o . VALUES ?s { … } } }` over the
|
||||
local union, constrained to the requested subjects (each entity's subject IRI IS
|
||||
its own document NURI). Returns the triples grouped per subject; the consumer maps
|
||||
them to its types (e.g. Festipod's `readEntities`). Reactivity = the consumer
|
||||
re-calls `readUnion` on its change signal (no reactive union query exists).
|
||||
|
||||
Reference in New Issue
Block a user