@lde/pipeline-shacl-sampler 0.9.4 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -160
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,167 +3,12 @@
|
|
|
3
3
|
Per-class sampling stages for [`@lde/pipeline`](../pipeline),
|
|
4
4
|
derived from SHACL shapes.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
[`Stage`](../pipeline/src/stage.ts) per `sh:targetClass`. Each stage
|
|
8
|
-
pairs an [`ItemSelector`](../pipeline/src/sparql/selector.ts) that picks
|
|
9
|
-
N instances of the target class from the distribution’s SPARQL endpoint
|
|
10
|
-
with a CONSTRUCT executor that, for every path chain the SHACL declares
|
|
11
|
-
(walked recursively through `sh:node`, `sh:class`,
|
|
12
|
-
`sh:qualifiedValueShape`, and `sh:or` branches, stopping at leaf
|
|
13
|
-
constraints or shape cycles), pulls in the triples reachable along that
|
|
14
|
-
chain’s terminal node. The resulting quads are a sample subgraph rich
|
|
15
|
-
enough for
|
|
16
|
-
[`@lde/pipeline-shacl-validator`](../pipeline-shacl-validator) to
|
|
17
|
-
validate without false-positive ‘missing nested node’ violations.
|
|
6
|
+
## Installation
|
|
18
7
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
import { Pipeline, FileWriter } from '@lde/pipeline';
|
|
23
|
-
import { shaclSampleStages } from '@lde/pipeline-shacl-sampler';
|
|
24
|
-
import { ShaclValidator } from '@lde/pipeline-shacl-validator';
|
|
25
|
-
|
|
26
|
-
const shapesFile =
|
|
27
|
-
'https://raw.githubusercontent.com/netwerk-digitaal-erfgoed/schema-profile/main/shacl.ttl';
|
|
28
|
-
const validator = new ShaclValidator({
|
|
29
|
-
shapesFile,
|
|
30
|
-
reportWriters: [new FileWriter({ outputDir: './validation', format: 'turtle' })],
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const stages = await shaclSampleStages({
|
|
34
|
-
shapesFile,
|
|
35
|
-
samplesPerClass: 50,
|
|
36
|
-
validator,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
await new Pipeline({ /* … */, stages }).run();
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Options
|
|
43
|
-
|
|
44
|
-
| Option | Default | Description |
|
|
45
|
-
| ------------------ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
46
|
-
| `shapesFile` | — | URL or local path to the SHACL shapes file. Any format `rdf-dereference` accepts. |
|
|
47
|
-
| `samplesPerClass` | `50` | Number of top-level resources to sample per `sh:targetClass`. |
|
|
48
|
-
| `timeout` | `60000` | SPARQL query timeout in milliseconds. |
|
|
49
|
-
| `batchSize` | `samplesPerClass` | Maximum sampled subjects per executor call. Lower values spread work across multiple parallel queries. |
|
|
50
|
-
| `maxConcurrency` | `10` | Maximum concurrent in-flight executor batches per stage. |
|
|
51
|
-
| `validator` | — | Optional [`Validator`](../pipeline/src/validator.ts) attached to every generated stage (typically a `ShaclValidator`). |
|
|
52
|
-
| `onInvalid` | `'write'` | Behaviour when a sampled batch fails validation: `'write'` \| `'skip'` \| `'halt'`. Only used when `validator` is set. |
|
|
53
|
-
| `namespaceAliases` | `[]` | Namespaces to treat as equivalent when matching `sh:targetClass` and when handing quads to the validator. See [Namespace aliases](#namespace-aliases). |
|
|
54
|
-
| `excludeResources` | — | Hook returning a SPARQL fragment that subtracts resources from a target class’s sample. See [Excluding resources](#excluding-resources). |
|
|
55
|
-
|
|
56
|
-
## Namespace aliases
|
|
57
|
-
|
|
58
|
-
Some vocabularies publish the same terms under multiple namespaces — most
|
|
59
|
-
notably schema.org, which is reachable at both `http://schema.org/` and
|
|
60
|
-
`https://schema.org/`. SHACL shapes can only declare one of those as the
|
|
61
|
-
`sh:targetClass` namespace, so without help the sampler would silently
|
|
62
|
-
skip resources typed under the other form, and the validator would
|
|
63
|
-
report vacuously-conformant runs against datasets that mix the two.
|
|
64
|
-
|
|
65
|
-
`namespaceAliases` closes the gap. For every declared pair the sampler:
|
|
66
|
-
|
|
67
|
-
- broadens its subject-selection SELECT to `?s a ?type . FILTER(?type IN
|
|
68
|
-
(<canonical/X>, <alias/X>))`, so instances typed under either
|
|
69
|
-
namespace are picked up;
|
|
70
|
-
- decorates the configured `validator` so any alias-namespace IRI in
|
|
71
|
-
the sampled quads is rewritten to the canonical form before the SHACL
|
|
72
|
-
engine sees it, allowing the canonical-namespace
|
|
73
|
-
`sh:targetClass` / `sh:path` patterns to match.
|
|
74
|
-
|
|
75
|
-
Defaults to no aliases. To cover schema.org datasets that publish under
|
|
76
|
-
both HTTP and HTTPS:
|
|
77
|
-
|
|
78
|
-
```ts
|
|
79
|
-
const stages = await shaclSampleStages({
|
|
80
|
-
shapesFile,
|
|
81
|
-
validator,
|
|
82
|
-
namespaceAliases: [
|
|
83
|
-
{ canonical: 'https://schema.org/', alias: 'http://schema.org/' },
|
|
84
|
-
],
|
|
85
|
-
});
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Excluding resources
|
|
89
|
-
|
|
90
|
-
A profile may target a class that also describes a dataset’s own
|
|
91
|
-
administrative metadata rather than its collection content. For example,
|
|
92
|
-
SCHEMA-AP-NDE targets `schema:Organization`, so a dump containing nothing
|
|
93
|
-
but a `schema:Dataset` and the publisher `Organization` that supports it
|
|
94
|
-
would get that publisher sampled, found trivially conformant (only `name`
|
|
95
|
-
is required), and reported as “tested and passed” — even though the
|
|
96
|
-
dataset holds no real content.
|
|
97
|
-
|
|
98
|
-
`excludeResources` lets the caller subtract such resources from a target
|
|
99
|
-
class’s sample **without weakening the SHACL shapes**. It is a hook called
|
|
100
|
-
once per `sh:targetClass`; the SPARQL fragment it returns is inlined
|
|
101
|
-
verbatim into the subject-selector `WHERE` clause **after** the type
|
|
102
|
-
pattern, so it can reference the bound `?s` (e.g. a `MINUS { … }` clause
|
|
103
|
-
that removes `?s` when it is the dataset’s publisher). Return `''` to
|
|
104
|
-
sample that class unchanged; omitting the option entirely leaves every
|
|
105
|
-
generated query byte-for-byte the same.
|
|
106
|
-
|
|
107
|
-
The fragment is interpolated verbatim and therefore caller-trusted — the
|
|
108
|
-
same trust model as a distribution’s `subjectFilter`.
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
const SCHEMA = 'https://schema.org/';
|
|
112
|
-
const stages = await shaclSampleStages({
|
|
113
|
-
shapesFile,
|
|
114
|
-
validator,
|
|
115
|
-
excludeResources: (targetClass) =>
|
|
116
|
-
targetClass.value === `${SCHEMA}Organization`
|
|
117
|
-
? `MINUS { ?dataset a <${SCHEMA}Dataset> ; <${SCHEMA}publisher> ?s . }`
|
|
118
|
-
: '',
|
|
119
|
-
});
|
|
8
|
+
```sh
|
|
9
|
+
npm install @lde/pipeline-shacl-sampler
|
|
120
10
|
```
|
|
121
11
|
|
|
122
|
-
|
|
123
|
-
alone) keeps genuine content resources in the sample: an `Organization`
|
|
124
|
-
reached as the `creator` of a `CreativeWork`, say, is never the dataset’s
|
|
125
|
-
publisher, so it is still sampled and validated.
|
|
126
|
-
|
|
127
|
-
## Limitations
|
|
128
|
-
|
|
129
|
-
- Only plain-IRI `sh:path` values are supported. Sequence, alternative
|
|
130
|
-
and inverse paths throw at extraction time.
|
|
131
|
-
- `sh:targetClass` is the only target form recognised; `sh:targetNode`,
|
|
132
|
-
`sh:targetSubjectsOf`, `sh:targetObjectsOf` and `sh:sparqlTarget` are
|
|
133
|
-
not yet supported.
|
|
134
|
-
|
|
135
|
-
## Related work
|
|
136
|
-
|
|
137
|
-
### `extract-cbd-shape`
|
|
138
|
-
|
|
139
|
-
The TREEcg / W3C TREE-incubation
|
|
140
|
-
[`extract-cbd-shape`](https://github.com/TREEcg/extract-cbd-shape)
|
|
141
|
-
library implements a per-entity walk over an in-memory `RdfStore`,
|
|
142
|
-
falling back to an HTTP dereference of the focus node whenever a
|
|
143
|
-
required path is missing from the local store. It is the right tool
|
|
144
|
-
for streaming hypermedia consumers (e.g. LDES clients) that already
|
|
145
|
-
hold a current context and can fetch more of it over HTTP.
|
|
146
|
-
|
|
147
|
-
It is the wrong tool for this package’s setting. We assemble sample
|
|
148
|
-
subgraphs against a remote SPARQL endpoint with millions of triples;
|
|
149
|
-
the per-entity round-trip pattern would issue _N samples × M target
|
|
150
|
-
classes_ dereferences per dataset and assumes content-negotiable
|
|
151
|
-
entity IRIs that resolve to RDF — rarely true for the cultural
|
|
152
|
-
heritage datasets this package was built for.
|
|
153
|
-
|
|
154
|
-
### SHACL2SPARQL
|
|
155
|
-
|
|
156
|
-
The Corman, Reutter & Savković 2019
|
|
157
|
-
[translation](https://link.springer.com/chapter/10.1007/978-3-030-30796-7_27)
|
|
158
|
-
of SHACL constraints to SPARQL targets validation, not sample
|
|
159
|
-
subgraph extraction. No production JavaScript implementation exists.
|
|
160
|
-
|
|
161
|
-
### Why batch CONSTRUCT per `sh:targetClass`
|
|
12
|
+
## Documentation
|
|
162
13
|
|
|
163
|
-
|
|
164
|
-
CONSTRUCT executor receives a batch of sampled subjects and walks
|
|
165
|
-
every path chain the SHACL declares server-side. A capable SPARQL
|
|
166
|
-
endpoint (e.g. QLever) evaluates the property-path UNIONs in a
|
|
167
|
-
single round-trip per batch, regardless of chain count. The
|
|
168
|
-
alternative — extracting per entity in the client — would multiply
|
|
169
|
-
round-trips by the sample size.
|
|
14
|
+
See the [full documentation](https://ldelements.org/reference/pipeline-shacl-sampler).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/pipeline-shacl-sampler",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"description": "Per-class sampling stages for @lde/pipeline, derived from SHACL shapes",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "git+https://github.com/ldelements/lde.git",
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@rdfjs/types": "^2.0.1",
|
|
29
29
|
"@tpluscode/rdf-ns-builders": "^5.0.0",
|
|
30
|
-
"n3": "^2.1.
|
|
30
|
+
"n3": "^2.1.2",
|
|
31
31
|
"rdf-dereference": "^5.0.0",
|
|
32
32
|
"tslib": "^2.3.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"@lde/dataset": "^0.7.9",
|
|
36
|
-
"@lde/pipeline": "^0.35.
|
|
36
|
+
"@lde/pipeline": "^0.35.6"
|
|
37
37
|
}
|
|
38
38
|
}
|