feat(pipeline)!: add beforeDatasetWrite hook; split namespace normalization#577
Merged
Conversation
745c367 to
133c76d
Compare
… a plugin Adds a generic per-dataset plugin hook and moves schema.org namespace normalization onto it, so the VoID analysis core no longer knows about namespace aliases. @lde/pipeline: - Add PipelinePlugin.beforeDatasetWrite: a QuadTransform run once over a whole dataset's cross-stage output before write. Implemented as a RunWriter decorator that streams every stage's output through one AsyncQueue and honors the flush/reset/commit/abort run lifecycle; nothing is materialized. - Remove the superseded object-only schemaOrgNormalizationPlugin and namespaceNormalizationPlugin (replaced by the pipeline-void plugin below). @lde/pipeline-void: - Add schemaOrgNormalizationPlugin (and generic namespaceNormalizationPlugin): a beforeDatasetWrite plugin that canonicalizes void:class/void:property and re-mints/merges partition IRIs from their canonical components. A whole-dataset view resolves each partition's cp->pp->dp chain from the other stages, so the queries need no markers or self-describe chains. - Revert the analysis queries to plain SPARQL; remove the namespaceAliases stage option, the class-selector canonicalization/expansion, query markers and void-ext self-describe chains added for the per-stage approach. - The merge streams: only partition quads are buffered (bounded by the summary), everything else passes through. Single-namespace datasets (the common case) keep every count exact; a dataset mixing both namespaces on one property has its void:distinctObjects summed (an over-count), which is documented and not optimized for. See ADR 8. BREAKING CHANGE: @lde/pipeline no longer exports schemaOrgNormalizationPlugin or namespaceNormalizationPlugin; use schemaOrgNormalizationPlugin from @lde/pipeline-void. @lde/pipeline-void's voidStages no longer accepts namespaceAliases; add schemaOrgNormalizationPlugin to the pipeline plugins instead.
133c76d to
db0d8bb
Compare
…tays in pipeline-void Split schema.org namespace handling by generality instead of removing the generic capability, so a non-VoID consumer (e.g. mapping instance data to an application profile) can still normalize a namespace. - @lde/pipeline keeps namespaceNormalizationPlugin / schemaOrgNormalizationPlugin, now a blanket, vocabulary-agnostic beforeStageWrite rewrite of a namespace’s IRIs in every term position (previously only void:class/void:property objects, and only in this package before this PR removed them). - @lde/pipeline-void renames its beforeDatasetWrite merge to schemaOrgPartitionMergePlugin / namespacePartitionMergePlugin, so a VoID consumer can’t pick the plain rewrite and leave the duplicate partition nodes (#334) unmerged. - Both share the canonicalizeIri primitive; the merge keeps its own selective canonicalization (leaves void:vocabulary and entity-properties in the source namespace), which the blanket rewrite deliberately does not. Docs: renumber the ADR to 7 and make it self-contained; document the split in the ADR and both package READMEs. BREAKING CHANGE: @lde/pipeline’s namespaceNormalizationPlugin and schemaOrgNormalizationPlugin now rewrite every matching IRI in any term position via beforeStageWrite, not just void:class/void:property objects. The VoID partition merge is now schemaOrgPartitionMergePlugin / namespacePartitionMergePlugin in @lde/pipeline-void. Claude-Session: https://claude.ai/code/session_01H8MVbsXSoNbYREYMFLy6Eg
ddeboer
added a commit
to netwerk-digitaal-erfgoed/dataset-knowledge-graph
that referenced
this pull request
Jul 9, 2026
Fixes the duplicate `void:classPartition` for schema.org classes (#334) in production by switching to the merge plugin released in ldelements/lde#577. ## What - Bump `@lde/pipeline` `0.33.4 → 0.34.0` and `@lde/pipeline-void` `0.31.4 → 0.32.0`. - Swap the pipeline plugin from `@lde/pipeline`’s generic `schemaOrgNormalizationPlugin` to `@lde/pipeline-void`’s `schemaOrgPartitionMergePlugin`. ## Why The old `@lde/pipeline` plugin only rewrote `void:class`/`void:property` objects `http → https`, which left **two** `void:classPartition` nodes for the same class when a dataset uses both `http://` and `https://schema.org/` – the duplicate that crashed the Dataset Register browser (#334). `schemaOrgPartitionMergePlugin` additionally re-mints and merges those partition nodes into one canonical partition per class. It runs as a `beforeDatasetWrite` transform over the whole dataset’s output, so the VoID analysis queries stay unaware of namespace aliases (see ldelements/lde#577 and its ADR 7). `voidStages()` and the SHACL sampler’s own `namespaceAliases` are unchanged. ## Reaching existing datasets DKG skips datasets whose data dump is unchanged. `PIPELINE_VERSION` is release-please–managed, so merging this `fix:` bumps the version, rotates the token, and forces a full reprocess on the next run – which is when the 4 currently-affected datasets (Venlo, literatuurmuseum, nijmegen studiezaal, ldmax wo2net; 4 of 542 measured) get single partitions. ## Validation `npm run compile`, `npm run lint`, `npm test` (148 tests) all pass locally. https://claude.ai/code/session_01H8MVbsXSoNbYREYMFLy6Eg
ddeboer
added a commit
to netwerk-digitaal-erfgoed/dataset-knowledge-graph
that referenced
this pull request
Jul 9, 2026
Fixes the duplicate `void:classPartition` for schema.org classes (#334) in production by switching to the merge plugin released in ldelements/lde#577. ## What - Bump `@lde/pipeline` `0.33.4 → 0.34.0` and `@lde/pipeline-void` `0.31.4 → 0.32.0`. - Swap the pipeline plugin from `@lde/pipeline`’s generic `schemaOrgNormalizationPlugin` to `@lde/pipeline-void`’s `schemaOrgPartitionMergePlugin`. ## Why The old `@lde/pipeline` plugin only rewrote `void:class`/`void:property` objects `http → https`, which left **two** `void:classPartition` nodes for the same class when a dataset uses both `http://` and `https://schema.org/` – the duplicate that crashed the Dataset Register browser (#334). `schemaOrgPartitionMergePlugin` additionally re-mints and merges those partition nodes into one canonical partition per class. It runs as a `beforeDatasetWrite` transform over the whole dataset’s output, so the VoID analysis queries stay unaware of namespace aliases (see ldelements/lde#577 and its ADR 7). `voidStages()` and the SHACL sampler’s own `namespaceAliases` are unchanged. ## Reaching existing datasets DKG skips datasets whose data dump is unchanged. `PIPELINE_VERSION` is release-please–managed, so merging this `fix:` bumps the version, rotates the token, and forces a full reprocess on the next run – which is when the 4 currently-affected datasets (Venlo, literatuurmuseum, nijmegen studiezaal, ldmax wo2net; 4 of 542 measured) get single partitions. ## Validation `npm run compile`, `npm run lint`, `npm test` (148 tests) all pass locally.
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.
Reworks the schema.org namespace normalization from #568 so the VoID analysis core no longer knows about namespace aliases – normalization is one opt-in plugin at the pipeline edge again, and the namespace handling is split by generality.
Why
#568 fixed the duplicate
void:classPartition(netwerk-digitaal-erfgoed/dataset-knowledge-graph#334) but threaded namespace-alias awareness through the core: a per-stage merge transform, a class selector that canonicalized and co-located variants per batch, query markers, and self-describingvoid-extchains. That coupled a consumer-specific concern into the analysis queries and stage machinery.Two facts reframe it: merging per-class partitions needs a class’s
http/httpsvariants seen together, whichbeforeStageWrite(per stage/batch) can’t provide – hence the co-location leaking into the core; and datasets typically use a single schema.org namespace, so the cross-variant merge is for the rare mixed dataset, not the common case.What
@lde/pipeline– a generic per-dataset hook, plus a generic namespace rewrite:PipelinePlugin.beforeDatasetWrite: aQuadTransformrun once over a whole dataset’s cross-stage output before write – the dataset-scoped sibling ofbeforeStageWrite. Implemented as aRunWriterdecorator that streams every stage’s output through oneAsyncQueueand honors theflush/reset/commit/abortrun lifecycle; nothing is materialized. Domain-agnostic – dedup, roll-ups, or partition merging can use it.namespaceNormalizationPlugin/schemaOrgNormalizationPlugin, now generalized into a blanket, vocabulary-agnosticbeforeStageWriterewrite of a namespace’s IRIs in every term position (previously onlyvoid:class/void:propertyobjects). This serves consumers that just standardize a namespace across a dataset’s own quads – e.g. mapping instance data to an application profile – with no VoID involved.@lde/pipeline-void– the VoID partition merge, core reverted:schemaOrgPartitionMergePlugin()(and genericnamespacePartitionMergePlugin(aliases)): abeforeDatasetWriteplugin that canonicalizesvoid:class/void:propertyand re-mints/merges the duplicate partition nodes from their canonical components. A whole-dataset view resolves each partition’scp → pp → dpchain from the other stages, so the queries need no markers or self-describe chains. Renamed so a VoID consumer can’t pick the plain rewrite and leave the duplicate nodes unmerged.namespaceAliasesstage option, class-selector canonicalization/expansion, query markers andvoid-extself-describe chains are gone.Why the split
The generic rewrite and the merge share only the
canonicalizeIriprimitive. Rewritingvoid:classobjects alone would still leave twovoid:classPartitionnodes for one class, so the merge (VoID-specific: it knows the partition vocabulary and the shared partition-IRI minting) can’t be replaced by the rewrite. And the merge’s canonicalization is deliberately selective – it leavesvoid:vocabularyand entity-properties in the source namespace – whereas the generic rewrite is a blanket pass. So the two stay separate plugins, in the package each belongs to.Correctness
Single-namespace datasets (the common case) keep every count exact – the plugin only renames and re-keys. A dataset that genuinely mixes both namespaces on one property has its
void:distinctObjectssummed (an over-count for shared objects), which is documented and not optimized for; entities/triples stay exact under subject-disjointness.Tested: the
beforeDatasetWritehook (streaming, flush, reset, abort) in@lde/pipeline; the generic rewrite plugin (all term positions) in@lde/pipeline; the merge transform + plugin (incl. the MD5 minting matching the queries, single-namespace exactness, and the mixed over-count) in@lde/pipeline-void; and an endpoint-backed test running the real plain queries through the plugin end-to-end.See ADR 7. Follow-up: dataset-knowledge-graph switches to
plugins: [schemaOrgPartitionMergePlugin(), provenancePlugin()].BREAKING CHANGE:
@lde/pipeline’snamespaceNormalizationPlugin/schemaOrgNormalizationPluginnow rewrite every matching IRI in any term position viabeforeStageWrite, not justvoid:class/void:propertyobjects. The VoID partition merge is nowschemaOrgPartitionMergePlugin/namespacePartitionMergePluginin@lde/pipeline-void, andvoidStagesno longer acceptsnamespaceAliases– add the plugin to the pipelinepluginsinstead.https://claude.ai/code/session_01H8MVbsXSoNbYREYMFLy6Eg