@lde/search-pipeline 0.14.0 → 0.15.1

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/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { searchIndexerPipeline } from './search-indexer-pipeline.js';
2
2
  export type { SearchIndexerPipelineOptions } from './search-indexer-pipeline.js';
3
- export { searchStages, selectByClass } from './search-stages.js';
3
+ export { registrySource, searchStages, selectByClass, } from './search-stages.js';
4
4
  export type { SearchStagesOptions, SearchStageType } from './search-stages.js';
5
5
  export { extractionQuery, extractionQueryString } from './extraction.js';
6
6
  export type { ExtractionOptions } from './extraction.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACjE,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACzE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACzE,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,EACL,cAAc,EACd,YAAY,EACZ,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACzE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACzE,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { searchIndexerPipeline } from './search-indexer-pipeline.js';
2
- export { searchStages, selectByClass } from './search-stages.js';
2
+ export { registrySource, searchStages, selectByClass, } from './search-stages.js';
3
3
  export { extractionQuery, extractionQueryString } from './extraction.js';
4
4
  export { searchIndexWriter } from './search-index-writer.js';
@@ -23,7 +23,7 @@ export interface SearchIndexWriterOptions {
23
23
  * own collection, alias and cross-pod lock. The per-collection fan-out is this
24
24
  * writer’s job.
25
25
  */
26
- writerFor: (searchType: RootType) => Writer<SearchDocument>;
26
+ writerFor: (searchType: RootType, schema: SearchSchema) => Writer<SearchDocument>;
27
27
  }
28
28
  /**
29
29
  * The single terminal of a search-indexing pipeline: an engine-agnostic router
@@ -1 +1 @@
1
- {"version":3,"file":"search-index-writer.d.ts","sourceRoot":"","sources":["../src/search-index-writer.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,SAAS,EAAE,CAAC,UAAU,EAAE,QAAQ,KAAK,MAAM,CAAC,cAAc,CAAC,CAAC;CAC7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,GAChC,MAAM,CAAC,mBAAmB,CAAC,CAoI7B"}
1
+ {"version":3,"file":"search-index-writer.d.ts","sourceRoot":"","sources":["../src/search-index-writer.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,SAAS,EAAE,CACT,UAAU,EAAE,QAAQ,EACpB,MAAM,EAAE,YAAY,KACjB,MAAM,CAAC,cAAc,CAAC,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,GAChC,MAAM,CAAC,mBAAmB,CAAC,CAoI7B"}
@@ -36,7 +36,7 @@ export function searchIndexWriter(options) {
36
36
  // by the type IRI, which is also how a paired document names its type.
37
37
  const writers = new Map([...schema.values()].map((searchType) => [
38
38
  searchType.class,
39
- writerFor(searchType),
39
+ writerFor(searchType, schema),
40
40
  ]));
41
41
  return {
42
42
  async openRun(context) {
@@ -29,9 +29,31 @@ export interface SearchIndexerPipelineOptions {
29
29
  /**
30
30
  * The engine writer that owns a given root type’s collection – e.g. a
31
31
  * `@lde/search-typesense` `InPlaceRebuild` or `BlueGreenRebuild` bound to
32
- * that type. Called once per {@link RootType} in the schema.
32
+ * that type. Called once per {@link RootType} in the schema, with the schema
33
+ * itself: an engine that stores a surfaced inline reference as a nested
34
+ * document needs it to declare the nesting, and it is the pipeline that holds
35
+ * it.
33
36
  */
34
- writerFor: (searchType: RootType) => Writer<SearchDocument>;
37
+ writerFor: (searchType: RootType, schema: SearchSchema) => Writer<SearchDocument>;
38
+ /**
39
+ * Root types whose triples come from the **dataset registry** rather than
40
+ * from each dataset’s own distribution – typically the dataset description
41
+ * itself and what hangs off it, which live in the register because
42
+ * registering a dataset is submitting a description
43
+ * ({@link registrySource}).
44
+ *
45
+ * Routing is deployment topology, so it is configured here and not declared
46
+ * on the {@link SearchType}: a type is defined by its `class`, not by where
47
+ * its triples come from. Name the types by their `name`; an unknown name
48
+ * throws here, at wiring time, rather than yielding an empty collection at
49
+ * run time.
50
+ */
51
+ registryTypes?: {
52
+ /** The registry’s SPARQL endpoint – where these types read. */
53
+ endpoint: URL;
54
+ /** {@link SearchType.name}s of the root types to source from it. */
55
+ names: readonly string[];
56
+ };
35
57
  /**
36
58
  * Optional per-dataset processing memory: skip a dataset whose source
37
59
  * fingerprint and {@link pipelineVersion} both match the stored record.
@@ -71,7 +93,8 @@ export interface SearchIndexerPipelineOptions {
71
93
  * ...createQlever({ mode: 'docker', image: 'adfreiburg/qlever:latest' }),
72
94
  * strategy: 'import',
73
95
  * }),
74
- * writerFor: (searchType) => new InPlaceRebuild(typesenseClient, searchType),
96
+ * writerFor: (searchType, schema) =>
97
+ * new InPlaceRebuild(typesenseClient, searchType, { schema }),
75
98
  * });
76
99
  * await pipeline.run();
77
100
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"search-indexer-pipeline.d.ts","sourceRoot":"","sources":["../src/search-indexer-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAEL,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,iDAAiD;AACjD,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;;;OAIG;IACH,QAAQ,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC;IACtC;;;;;;;;OAQG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C;;;;OAIG;IACH,SAAS,EAAE,CAAC,UAAU,EAAE,QAAQ,KAAK,MAAM,CAAC,cAAc,CAAC,CAAC;IAC5D;;;;OAIG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,GAAG,SAAS,gBAAgB,EAAE,CAAC;CAC3D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,4BAA4B,GACpC,QAAQ,CAAC,mBAAmB,CAAC,CAoB/B"}
1
+ {"version":3,"file":"search-indexer-pipeline.d.ts","sourceRoot":"","sources":["../src/search-indexer-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAEL,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,iDAAiD;AACjD,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;;;OAIG;IACH,QAAQ,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC;IACtC;;;;;;;;OAQG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C;;;;;;;OAOG;IACH,SAAS,EAAE,CACT,UAAU,EAAE,QAAQ,EACpB,MAAM,EAAE,YAAY,KACjB,MAAM,CAAC,cAAc,CAAC,CAAC;IAC5B;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE;QACd,+DAA+D;QAC/D,QAAQ,EAAE,GAAG,CAAC;QACd,oEAAoE;QACpE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;KAC1B,CAAC;IACF;;;;OAIG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,GAAG,SAAS,gBAAgB,EAAE,CAAC;CAC3D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,4BAA4B,GACpC,QAAQ,CAAC,mBAAmB,CAAC,CA0B/B"}
@@ -1,6 +1,6 @@
1
1
  import { ManualDatasetSelection, Pipeline, } from '@lde/pipeline';
2
2
  import { searchIndexWriter } from './search-index-writer.js';
3
- import { searchStages, selectByClass } from './search-stages.js';
3
+ import { registrySource, searchStages, selectByClass, } from './search-stages.js';
4
4
  /**
5
5
  * Wire the common **object-grain** search indexer and return the ready-to-run
6
6
  * {@link Pipeline}: one projecting stage per {@link RootType} in the schema,
@@ -22,7 +22,8 @@ import { searchStages, selectByClass } from './search-stages.js';
22
22
  * ...createQlever({ mode: 'docker', image: 'adfreiburg/qlever:latest' }),
23
23
  * strategy: 'import',
24
24
  * }),
25
- * writerFor: (searchType) => new InPlaceRebuild(typesenseClient, searchType),
25
+ * writerFor: (searchType, schema) =>
26
+ * new InPlaceRebuild(typesenseClient, searchType, { schema }),
26
27
  * });
27
28
  * await pipeline.run();
28
29
  * ```
@@ -34,7 +35,11 @@ import { searchStages, selectByClass } from './search-stages.js';
34
35
  * owns no capability of its own.
35
36
  */
36
37
  export function searchIndexerPipeline(options) {
37
- const { schema, datasets } = options;
38
+ const { schema, datasets, registryTypes } = options;
39
+ const fromRegistry = registryTypeNames(schema, registryTypes?.names);
40
+ const sourceFor = registryTypes === undefined
41
+ ? undefined
42
+ : registrySource(registryTypes.endpoint);
38
43
  return new Pipeline({
39
44
  datasetSelector: Array.isArray(datasets)
40
45
  ? new ManualDatasetSelection(datasets)
@@ -46,6 +51,7 @@ export function searchIndexerPipeline(options) {
46
51
  searchType,
47
52
  rootVariable: 'root',
48
53
  itemSelector: selectByClass(searchType),
54
+ sourceFor: fromRegistry.has(searchType.name) ? sourceFor : undefined,
49
55
  })),
50
56
  }),
51
57
  writers: searchIndexWriter({ schema, writerFor: options.writerFor }),
@@ -54,3 +60,22 @@ export function searchIndexerPipeline(options) {
54
60
  reporter: options.reporter,
55
61
  });
56
62
  }
63
+ /**
64
+ * The declared registry-sourced type names, validated against the schema. An
65
+ * unrecognised name is a configuration mistake that would otherwise be silent –
66
+ * the type would read the dataset’s distribution, find no dataset description
67
+ * there, and ship an empty collection – so it throws at wiring time.
68
+ */
69
+ function registryTypeNames(schema, declaredNames) {
70
+ const names = new Set(declaredNames ?? []);
71
+ const declared = new Set([...schema.values()].map((searchType) => searchType.name));
72
+ const unknown = [...names].filter((name) => !declared.has(name));
73
+ if (unknown.length > 0) {
74
+ throw new Error(`Unknown registry root type(s) ${unknown
75
+ .map((name) => `“${name}”`)
76
+ .join(', ')}; the schema declares ${[...declared]
77
+ .map((name) => `“${name}”`)
78
+ .join(', ')}.`);
79
+ }
80
+ return names;
81
+ }
@@ -1,4 +1,4 @@
1
- import { Stage, type ItemSelector, type StageReaders } from '@lde/pipeline';
1
+ import { Stage, type ItemSelector, type StageOptions, type StageReaders } from '@lde/pipeline';
2
2
  import { type RootType, type SearchSchema } from '@lde/search';
3
3
  import type { TypedSearchDocument } from './typed-search-document.js';
4
4
  /** One root type’s stage in a search pipeline. */
@@ -34,6 +34,13 @@ export interface SearchStageType {
34
34
  * readers, or attach transforms.
35
35
  */
36
36
  readers?: StageReaders;
37
+ /**
38
+ * Where this type’s stage reads, when that is not the dataset’s own
39
+ * distribution – see {@link StageOptions.sourceFor}. {@link registrySource}
40
+ * builds the one that matters here: a root type described by the **dataset
41
+ * registry** rather than by the dataset’s data.
42
+ */
43
+ sourceFor?: StageOptions<TypedSearchDocument>['sourceFor'];
37
44
  /**
38
45
  * Roots (and so documents) per batch – the memory bound. Under a root-bound
39
46
  * selector it moves memory and request count, never output.
@@ -94,4 +101,44 @@ export declare function searchStages(options: SearchStagesOptions): Stage<TypedS
94
101
  * SPARQL reader).
95
102
  */
96
103
  export declare function selectByClass(searchType: RootType, rootVariable?: string): ItemSelector;
104
+ /**
105
+ * A {@link SearchStageType.sourceFor} that reads a root type from the **dataset
106
+ * registry** instead of from the dataset’s own data – the endpoint fixed,
107
+ * scoped to the graph the dataset in hand names.
108
+ *
109
+ * A dataset’s description is governed by a different application profile from
110
+ * the objects it contains, and it lives in the register, not in the
111
+ * distribution: registering a dataset *is* submitting a description, so the
112
+ * register is the one source that covers every dataset a pipeline can select.
113
+ * Nothing obliges a publisher to ship a description of the dataset in its own
114
+ * dump.
115
+ *
116
+ * The graph scoping is what keeps the stage per-dataset. A register holds every
117
+ * registration, so an unscoped stage would index the whole catalogue for each
118
+ * dataset processed; scoped, each pass sees exactly the one registration – and
119
+ * the roots a `selectByClass` finds inside that graph are that dataset’s own.
120
+ * It presumes the register names each registration’s graph after the dataset
121
+ * IRI, which is how a DCAT register that crawls per registration stores it (the
122
+ * NDE Dataset Register does so for every registration it holds).
123
+ *
124
+ * Routing is deployment topology, deliberately kept out of the
125
+ * {@link SearchType}: a type is defined by its `class`, not by where its
126
+ * triples come from, so the same declaration serves a deployment that sources
127
+ * it differently.
128
+ *
129
+ * ```ts
130
+ * searchStages({
131
+ * schema,
132
+ * types: [...schema.values()].map((searchType) => ({
133
+ * searchType,
134
+ * rootVariable: 'root',
135
+ * itemSelector: selectByClass(searchType),
136
+ * sourceFor: registryTypeNames.has(searchType.name)
137
+ * ? registrySource(registryEndpoint)
138
+ * : undefined,
139
+ * })),
140
+ * });
141
+ * ```
142
+ */
143
+ export declare function registrySource(endpoint: URL): NonNullable<SearchStageType['sourceFor']>;
97
144
  //# sourceMappingURL=search-stages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-stages.d.ts","sourceRoot":"","sources":["../src/search-stages.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAgB,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAE7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,UAAU,EAAE,QAAQ,CAAC;IACrB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,YAAY,EAAE,YAAY,CAAC;IAC3B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB,4DAA4D;IAC5D,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,mBAAmB,GAC3B,KAAK,CAAC,mBAAmB,CAAC,EAAE,CAwD9B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,QAAQ,EACpB,YAAY,SAAS,GACpB,YAAY,CAId"}
1
+ {"version":3,"file":"search-stages.d.ts","sourceRoot":"","sources":["../src/search-stages.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAgB,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAE7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,UAAU,EAAE,QAAQ,CAAC;IACrB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,YAAY,EAAE,YAAY,CAAC;IAC3B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC,WAAW,CAAC,CAAC;IAC3D;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB,4DAA4D;IAC5D,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,mBAAmB,GAC3B,KAAK,CAAC,mBAAmB,CAAC,EAAE,CAyD9B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,QAAQ,EACpB,YAAY,SAAS,GACpB,YAAY,CAId;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,GAAG,GACZ,WAAW,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC,CAE3C"}
@@ -1,3 +1,4 @@
1
+ import { Distribution } from '@lde/dataset';
1
2
  import { SparqlConstructReader, SparqlItemSelector, Stage, } from '@lde/pipeline';
2
3
  import { projectRoots } from '@lde/search';
3
4
  import { extractionQueryString } from './extraction.js';
@@ -39,6 +40,7 @@ export function searchStages(options) {
39
40
  name: searchType.name,
40
41
  readers,
41
42
  itemSelector: type.itemSelector,
43
+ sourceFor: type.sourceFor,
42
44
  batchSize: type.batchSize,
43
45
  maxConcurrency: type.maxConcurrency,
44
46
  queueCapacity: type.queueCapacity,
@@ -86,3 +88,45 @@ export function selectByClass(searchType, rootVariable = 'root') {
86
88
  query: `SELECT ?${rootVariable} WHERE { ?${rootVariable} a <${searchType.class}> FILTER(!isBlank(?${rootVariable})) }`,
87
89
  });
88
90
  }
91
+ /**
92
+ * A {@link SearchStageType.sourceFor} that reads a root type from the **dataset
93
+ * registry** instead of from the dataset’s own data – the endpoint fixed,
94
+ * scoped to the graph the dataset in hand names.
95
+ *
96
+ * A dataset’s description is governed by a different application profile from
97
+ * the objects it contains, and it lives in the register, not in the
98
+ * distribution: registering a dataset *is* submitting a description, so the
99
+ * register is the one source that covers every dataset a pipeline can select.
100
+ * Nothing obliges a publisher to ship a description of the dataset in its own
101
+ * dump.
102
+ *
103
+ * The graph scoping is what keeps the stage per-dataset. A register holds every
104
+ * registration, so an unscoped stage would index the whole catalogue for each
105
+ * dataset processed; scoped, each pass sees exactly the one registration – and
106
+ * the roots a `selectByClass` finds inside that graph are that dataset’s own.
107
+ * It presumes the register names each registration’s graph after the dataset
108
+ * IRI, which is how a DCAT register that crawls per registration stores it (the
109
+ * NDE Dataset Register does so for every registration it holds).
110
+ *
111
+ * Routing is deployment topology, deliberately kept out of the
112
+ * {@link SearchType}: a type is defined by its `class`, not by where its
113
+ * triples come from, so the same declaration serves a deployment that sources
114
+ * it differently.
115
+ *
116
+ * ```ts
117
+ * searchStages({
118
+ * schema,
119
+ * types: [...schema.values()].map((searchType) => ({
120
+ * searchType,
121
+ * rootVariable: 'root',
122
+ * itemSelector: selectByClass(searchType),
123
+ * sourceFor: registryTypeNames.has(searchType.name)
124
+ * ? registrySource(registryEndpoint)
125
+ * : undefined,
126
+ * })),
127
+ * });
128
+ * ```
129
+ */
130
+ export function registrySource(endpoint) {
131
+ return (dataset) => Distribution.sparql(endpoint, dataset.iri.toString());
132
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search-pipeline",
3
- "version": "0.14.0",
3
+ "version": "0.15.1",
4
4
  "description": "Search indexing as an @lde/pipeline instance: a Writer<Quad> that applies the @lde/search projection to extracted RDF and fans each document out to the transactional engine writer for its type's collection. Glue between the pipeline quad world and the search-engine document world; owns no projection or engine logic itself.",
5
5
  "repository": {
6
6
  "url": "git+https://github.com/ldelements/lde.git",
@@ -25,21 +25,21 @@
25
25
  "!**/*.tsbuildinfo"
26
26
  ],
27
27
  "dependencies": {
28
- "@lde/search": "^0.14.0",
28
+ "@lde/search": "^0.15.0",
29
29
  "@traqula/generator-sparql-1-1": "^1.1.8",
30
30
  "@traqula/parser-sparql-1-1": "^1.1.5",
31
31
  "@traqula/rules-sparql-1-1": "^1.1.0",
32
32
  "tslib": "^2.3.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@lde/search-typesense": "^0.17.0",
35
+ "@lde/search-typesense": "^0.18.1",
36
36
  "@rdfjs/types": "^2.0.1",
37
- "n3": "^2.1.1",
37
+ "n3": "^2.1.2",
38
38
  "testcontainers": "^12.0.3",
39
39
  "typesense": "^3.0.6"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@lde/dataset": "^0.7.9",
43
- "@lde/pipeline": "^0.35.4"
43
+ "@lde/pipeline": "^0.35.5"
44
44
  }
45
45
  }