feat(search)!: implement inline references and class-less reference types#629
Merged
Conversation
…ypes - Split SearchType into RootType (declares a class) and ReferenceType (no class). searchSchema partitions them, so only root types key the class map and earn a collection; reference types resolve an inline ref.typeName by name. - Implement the inline strategy in projection: a referent is projected through its reference type as a nested Search Document (an array for an array reference), recursing to the depth the schema declares. A role-less inline reference is a reading device, pruned before the writer; an output one surfaces the nesting. - frameSubjects gains a depth parameter and follows the reference graph breadth-first; projectRoots derives the depth from the schema, floored at one. - searchSchema validates that every inline ref.typeName resolves to a declared reference type and rejects inline cycles, keeping framing depth bounded. - Constrain the engine port and the Typesense adapter to root types, so an indexed reference type fails to compile. BREAKING CHANGE: class is now optional on SearchType (a reference type declares none); an inline ref.typeName is resolved and cycle-checked at searchSchema construction.
…ne referents - Prune internal (no-role) fields at every depth: a surfaced (output) inline reference now has its referent's internal helper fields removed before the writer, so the "a field without a role reaches neither the engine nor the API" invariant holds inside nested documents, not just at the root. - Pruning runs as one post-order pass after all projection, so a derive at any depth still reads a helper field before it is removed. - Mark ADR 11 Accepted.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #608.
Realises the
inlinereference strategy that ADR 8 forward-declared andADR 11 decided:
reach a qualified or deep RDF value by inlining a reference and deriving from it,
never by growing the path grammar. Builds on #607 (the
path/derivesplit andinternal fields).
What changed
Reference types –
SearchTypeis nowRootType | ReferenceType, toldapart by one absence: a Root Type declares a
class(indexed, keys theschema, earns a collection); a Reference Type declares none and is reached
only through an inline reference.
searchSchemapartitions its arguments –root types key the class map, reference types go into a name index an inline
ref.typeNameresolves against. Becauseschema.values()yields only roottypes, no writer ever opens a run/collection for a reference type, and an
indexed reference type fails to compile (the engine port and Typesense
adapter are constrained to root types).
strategy: 'inline'– projection now carries a referent’s own projectedfields. A referent is projected through its reference type as a nested
SearchDocument(an array for anarrayreference), recursing to the depththe schema declares. Roles decide whether the nesting surfaces: a role-less
inline reference is a reading device (internal, projected so a
derivecanflatten a value a path cannot address, then pruned before the writer); an
outputone is an API device that surfaces the nested type. So RDF depthand API shape stay independent.
Framing depth –
frameSubjectsgains adepthparameter and follows thereference graph breadth-first (visiting each subject once, so a data cycle
terminates).
projectRootsderives the depth from the schema(
inlineFramingDepth), floored at one hop, so the existing single-hop embedis preserved. Depth is a bounded property of the declaration, not a knob.
Validation –
searchSchemanow checks that every inlineref.typeNameresolves to a declared reference type and rejects inline cycles (the only way
framing depth could be unbounded), at construction rather than index time.
Scope
idOnlystays a forward declaration; onlyinlineis implemented here. TheDataset Register migration (a separate repo, gated on #548) is out of scope –
this PR builds the LDE mechanism only.
Breaking
classis now optional onSearchType, and inlinerefresolution is enforcedat
searchSchemaconstruction – a breaking@lde/searchAPI change (feat!).