feat(search)!: generate extraction CONSTRUCTs from the search schema#630
Open
ddeboer wants to merge 3 commits into
Open
feat(search)!: generate extraction CONSTRUCTs from the search schema#630ddeboer wants to merge 3 commits into
ddeboer wants to merge 3 commits into
Conversation
- Add irAlias(searchType, field) → urn:lde:‹Type›/‹field›, the minted extraction predicate both the reader and the projection agree on, beside physicalFields in the adapter surface. - Flip the projection to read node[irAlias(type, field)] instead of node[field.path]: path becomes purely source-side (the reader adapter’s grammar), threading the declaring type down so a root field mints against the root type and an inline referent’s field against its reference type. - Update the projection and framing test fixtures to key framed nodes by the minted alias rather than the source predicate. BREAKING CHANGE: the projection now reads each value under its field’s IR alias, not its source path; a reader must emit values under irAlias(type, field). path no longer keys the framed node.
…h schema - Add extractionQuery / extractionQueryString: a pure SearchType → QueryConstruct generator (Traqula AstFactory) that mints one IR-Alias template triple per path-bearing field, a UNION branch per field reading its source path as a SPARQL property path, a free subject for the pipeline’s VALUES injection, and a nested template for inline references (recursing to the schema’s declared depth). - Default a stage’s reader to this generated Extraction CONSTRUCT: readers on SearchStageType is now optional, so a SPARQL deployment need not hand-write the query, and reader and projection agree by construction on the alias set. - Add a schema↔CONSTRUCT contract test (the minted alias set equals the field set the projection reads) and an end-to-end round-trip against a local SPARQL endpoint (generate → read → frame → project). - Drop the stale sparqljs entry from AGENTS.md Key Dependencies and register the round-trip endpoint port. BREAKING CHANGE: SearchStageType.readers is now optional and defaults to the generated Extraction CONSTRUCT; a stage relying on the default must declare its fields’ source paths in SPARQL property-path grammar.
… non-deduplicating engines - Document that the generated extraction CONSTRUCTs (UNION-per-field, given roots, single-subject template, no projected-away constant triple) emit one triple per genuine solution on a non-deduplicating engine such as QLever, so there is no multiplicative CONSTRUCT inflation. - Note that this removes the need for a client-side post-processing dedup pass, which would defeat the batch-bounded streaming memory model; the residual linear duplication from duplicate input roots is absorbed by the streaming per-quad subject index as a cheap backstop.
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 #548.
Retires the hand-written extraction CONSTRUCTs by generating extraction from the search schema, and closes the loop the projection was already reaching for: the reader and the projection now agree on their intermediate vocabulary by construction, so they cannot drift.
What changed
@lde/search(breaking) – projection reads the IR Alias, not the source pathirAlias(searchType, field)→urn:lde:‹Type›/‹field›besidephysicalFieldsin the adapter surface: the one home for the minted-predicate convention both the reader and the projection must agree on.node[irAlias(type, field)]instead ofnode[field.path].pathbecomes purely source-side (the reader adapter’s grammar); the declaring type is threaded down, so a root field mints against the root type and an inline referent’s field against its reference type. A subject that is a root of two types no longer collides on a shared field name.@lde/search-pipeline(breaking) – the Extraction generatorextractionQuery/extractionQueryString: a pureSearchType → QueryConstructgenerator built with Traqula’sAstFactory(never sparqljs), stringified with the TraqulaGenerator. It mints:?root <irAlias> ?valuetriple per path-bearing field – IRIs only, single subject (a property path cannot be a CONSTRUCT verb, so the flattened value is minted onto its subject);?root <path> ?value), each field’spathlifted into a literal SPARQL property path (sequence / alternative / inverse) – UNION, never a conjunction, so a multi-valued field does not cross-multiply;?rootsubject for the pipeline’s existinginjectValues(VALUES ?root { … }) – root selection stays a separate, deployment concern (selectByClass/ the stage’sitemSelector);{ ?root <…/ref> ?r . ?r <…/field> ?v }), recursing to the schema’s declared depth.SearchStageType.readersis now optional: a SPARQL stage defaults to aSparqlConstructReaderrunning the generated Extraction CONSTRUCT, so the query no longer has to be hand-passed.Verification
@lde/local-sparql-endpointproves generate → read → frame → project on a Drapo-shapedCreativeWork/Personschema – including the default (schema-generated) reader wiring. The Drapo dump never enters CI.https://data.drapo.nl/dataset/schemaorg.export.nq, ~20 MB, 112 008 quads) was loaded into a local Comunica endpoint and the generated CONSTRUCTs run over the real roots (given-roots + UNION-per-field), matching whatsearchStagesproduces:name1 073,description814,creator382), raw == distinct: zero duplicates.labeltriples, raw == distinct: zero duplicates.Personsubjectsa schema:Personmore than once: a non-DISTINCTselector returns each such root twice, so given-roots repeat and the output triples repeat 1:1 with the duplicate roots – linear, not the multiplicative explosion the issue warns about (a template that projects away WHERE variables, e.g. a constant?s a <Type>, which this generator never emits). The pipeline collapses it regardless:projectRootsdedups roots vianew Set, andbuildSubjectIndexdedups triples. Re-running Person over the 158 distinct roots gives 158 raw = 158 distinct, confirming the template is duplicate-free per root.labelOnlywith single-predicate paths – no inline references, no property paths. The nested-template?s → ?rlink and its QLever-dedup risk are therefore covered by synthetic fixtures only; an at-scale check awaits a consumer that inlines (e.g. the Dataset Register). The generator builds the full mechanism regardless, so the contract test covers the whole model.Out of scope / for your eyes
pathin SPARQL property-path grammar (<iri>,<a>/<b>,^<iri>,<a>|<b>); a bare IRI without angle brackets makes the parser throw. The projection itself no longer parsespath, so hand-passed readers are unaffected.