@lde/search-pipeline 0.17.0 → 0.18.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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Dataset } from '@lde/dataset';
|
|
2
|
-
import { Pipeline, type DatasetSelector, type DistributionResolver, type ProgressReporter, type ProvenanceStore, type Writer } from '@lde/pipeline';
|
|
2
|
+
import { Pipeline, type DatasetSelector, type DistributionResolver, type ProgressReporter, type ProvenanceStore, type QuadTransform, type ReaderContext, type Writer } from '@lde/pipeline';
|
|
3
3
|
import type { RootType, SearchDocument, SearchSchema } from '@lde/search';
|
|
4
4
|
import type { TypedSearchDocument } from './typed-search-document.js';
|
|
5
5
|
/** Options for {@link searchIndexerPipeline}. */
|
|
@@ -54,6 +54,21 @@ export interface SearchIndexerPipelineOptions {
|
|
|
54
54
|
/** {@link SearchType.name}s of the root types to source from it. */
|
|
55
55
|
names: readonly string[];
|
|
56
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* {@link QuadTransform}(s) attached to a root type’s reader, keyed by
|
|
59
|
+
* {@link SearchType.name} – the extension point a deployment reaches for
|
|
60
|
+
* when the data needs correcting, enriching or pruning before projection.
|
|
61
|
+
*
|
|
62
|
+
* This is what keeps a deployment with one bespoke transform on this
|
|
63
|
+
* convenience instead of hand-composing a {@link Pipeline}: composing by hand
|
|
64
|
+
* to reach the reader means restating dataset selection, registry sourcing,
|
|
65
|
+
* provenance and reporting, and silently losing whichever is forgotten.
|
|
66
|
+
*
|
|
67
|
+
* Named by `name` rather than by class IRI, like {@link registryTypes}; a
|
|
68
|
+
* name the schema does not declare throws at wiring time rather than
|
|
69
|
+
* attaching nothing and shipping an unenriched collection.
|
|
70
|
+
*/
|
|
71
|
+
transforms?: Readonly<Record<string, QuadTransform<ReaderContext> | QuadTransform<ReaderContext>[]>>;
|
|
57
72
|
/**
|
|
58
73
|
* Optional per-dataset processing memory: skip a dataset whose source
|
|
59
74
|
* fingerprint and {@link pipelineVersion} both match the stored record.
|
|
@@ -99,11 +114,15 @@ export interface SearchIndexerPipelineOptions {
|
|
|
99
114
|
* await pipeline.run();
|
|
100
115
|
* ```
|
|
101
116
|
*
|
|
117
|
+
* Domain behaviour on top of the standard wiring is {@link transforms}, so
|
|
118
|
+
* adding it costs the behaviour rather than this whole composition.
|
|
119
|
+
*
|
|
102
120
|
* A deployment that needs more – a bespoke root selector (the entry point is a
|
|
103
121
|
* domain fact, not a class), per-stage tuning (`batchSize`, `maxConcurrency`),
|
|
104
122
|
* non-SPARQL readers, or quad-level plugins – composes {@link searchStages},
|
|
105
|
-
* {@link searchIndexWriter} and {@link Pipeline} directly
|
|
106
|
-
*
|
|
123
|
+
* {@link searchIndexWriter} and {@link Pipeline} directly, restating the
|
|
124
|
+
* registry sourcing, provenance and reporting this function wires; beyond
|
|
125
|
+
* those, this convenience owns no capability of its own.
|
|
107
126
|
*/
|
|
108
127
|
export declare function searchIndexerPipeline(options: SearchIndexerPipelineOptions): Pipeline<TypedSearchDocument>;
|
|
109
128
|
//# sourceMappingURL=search-indexer-pipeline.d.ts.map
|
|
@@ -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;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
|
|
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,aAAa,EAClB,KAAK,aAAa,EAClB,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;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,QAAQ,CACnB,MAAM,CACJ,MAAM,EACN,aAAa,CAAC,aAAa,CAAC,GAAG,aAAa,CAAC,aAAa,CAAC,EAAE,CAC9D,CACF,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,4BAA4B,GACpC,QAAQ,CAAC,mBAAmB,CAAC,CA4B/B"}
|
|
@@ -28,15 +28,20 @@ import { registrySource, searchStages, selectByClass, } from './search-stages.js
|
|
|
28
28
|
* await pipeline.run();
|
|
29
29
|
* ```
|
|
30
30
|
*
|
|
31
|
+
* Domain behaviour on top of the standard wiring is {@link transforms}, so
|
|
32
|
+
* adding it costs the behaviour rather than this whole composition.
|
|
33
|
+
*
|
|
31
34
|
* A deployment that needs more – a bespoke root selector (the entry point is a
|
|
32
35
|
* domain fact, not a class), per-stage tuning (`batchSize`, `maxConcurrency`),
|
|
33
36
|
* non-SPARQL readers, or quad-level plugins – composes {@link searchStages},
|
|
34
|
-
* {@link searchIndexWriter} and {@link Pipeline} directly
|
|
35
|
-
*
|
|
37
|
+
* {@link searchIndexWriter} and {@link Pipeline} directly, restating the
|
|
38
|
+
* registry sourcing, provenance and reporting this function wires; beyond
|
|
39
|
+
* those, this convenience owns no capability of its own.
|
|
36
40
|
*/
|
|
37
41
|
export function searchIndexerPipeline(options) {
|
|
38
|
-
const { schema, datasets, registryTypes } = options;
|
|
42
|
+
const { schema, datasets, registryTypes, transforms } = options;
|
|
39
43
|
const fromRegistry = registryTypeNames(schema, registryTypes?.names);
|
|
44
|
+
assertDeclaredTypeNames(schema, Object.keys(transforms ?? {}), 'transform');
|
|
40
45
|
const sourceFor = registryTypes === undefined
|
|
41
46
|
? undefined
|
|
42
47
|
: registrySource(registryTypes.endpoint);
|
|
@@ -52,6 +57,7 @@ export function searchIndexerPipeline(options) {
|
|
|
52
57
|
rootVariable: 'root',
|
|
53
58
|
itemSelector: selectByClass(searchType),
|
|
54
59
|
sourceFor: fromRegistry.has(searchType.name) ? sourceFor : undefined,
|
|
60
|
+
transform: transforms?.[searchType.name],
|
|
55
61
|
})),
|
|
56
62
|
}),
|
|
57
63
|
writers: searchIndexWriter({ schema, writerFor: options.writerFor }),
|
|
@@ -68,14 +74,23 @@ export function searchIndexerPipeline(options) {
|
|
|
68
74
|
*/
|
|
69
75
|
function registryTypeNames(schema, declaredNames) {
|
|
70
76
|
const names = new Set(declaredNames ?? []);
|
|
77
|
+
assertDeclaredTypeNames(schema, [...names], 'registry root');
|
|
78
|
+
return names;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Every name must be one the schema declares. Shared by the options that key
|
|
82
|
+
* behaviour by {@link SearchType.name}: naming a type the schema does not have
|
|
83
|
+
* is a configuration mistake whose only other symptom is an option that
|
|
84
|
+
* silently does nothing.
|
|
85
|
+
*/
|
|
86
|
+
function assertDeclaredTypeNames(schema, names, label) {
|
|
71
87
|
const declared = new Set([...schema.values()].map((searchType) => searchType.name));
|
|
72
|
-
const unknown =
|
|
88
|
+
const unknown = names.filter((name) => !declared.has(name));
|
|
73
89
|
if (unknown.length > 0) {
|
|
74
|
-
throw new Error(`Unknown
|
|
90
|
+
throw new Error(`Unknown ${label} type(s) ${unknown
|
|
75
91
|
.map((name) => `“${name}”`)
|
|
76
92
|
.join(', ')}; the schema declares ${[...declared]
|
|
77
93
|
.map((name) => `“${name}”`)
|
|
78
94
|
.join(', ')}.`);
|
|
79
95
|
}
|
|
80
|
-
return names;
|
|
81
96
|
}
|
package/dist/search-stages.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Stage, type ItemSelector, type StageOptions, type StageReaders } from '@lde/pipeline';
|
|
1
|
+
import { Stage, type ItemSelector, type QuadTransform, type ReaderContext, 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. */
|
|
@@ -30,10 +30,27 @@ export interface SearchStageType {
|
|
|
30
30
|
* from `searchType` ({@link extractionQuery}), with `rootVariable` as the free
|
|
31
31
|
* subject the batch’s roots bind to – the schema-derived reader the projection
|
|
32
32
|
* is guaranteed to agree with (they share the {@link irAlias} convention).
|
|
33
|
-
* Supply your own only to read from a non-SPARQL source
|
|
34
|
-
* readers,
|
|
33
|
+
* Supply your own only to read from a non-SPARQL source or merge several
|
|
34
|
+
* readers; to enrich the default reader’s quads, use {@link transform}.
|
|
35
35
|
*/
|
|
36
36
|
readers?: StageReaders;
|
|
37
|
+
/**
|
|
38
|
+
* {@link QuadTransform}(s) attached to the **default** reader – the usual way
|
|
39
|
+
* to add behaviour to this stage: correct the data, mint a quad the source
|
|
40
|
+
* does not ship, drop one it should not.
|
|
41
|
+
*
|
|
42
|
+
* Attaching here rather than building the reader yourself is what keeps the
|
|
43
|
+
* reader’s `subjectVariable` and this stage’s {@link rootVariable} in
|
|
44
|
+
* agreement: nothing cross-checks them, and a mismatch extracts nothing.
|
|
45
|
+
* Mutually exclusive with {@link readers} – a caller supplying its own
|
|
46
|
+
* reader(s) attaches transforms to them directly ({@link AttachedReader}),
|
|
47
|
+
* since only that caller knows which of several readers each belongs to.
|
|
48
|
+
*
|
|
49
|
+
* A field a transform fills must still declare a `path`: projection skips a
|
|
50
|
+
* field with neither a `path` nor a `derive`, so a transform-minted IR Alias
|
|
51
|
+
* is otherwise never read.
|
|
52
|
+
*/
|
|
53
|
+
transform?: QuadTransform<ReaderContext> | QuadTransform<ReaderContext>[];
|
|
37
54
|
/**
|
|
38
55
|
* Where this type’s stage reads, when that is not the dataset’s own
|
|
39
56
|
* distribution – see {@link StageOptions.sourceFor}. {@link registrySource}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-stages.d.ts","sourceRoot":"","sources":["../src/search-stages.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,
|
|
1
|
+
{"version":3,"file":"search-stages.d.ts","sourceRoot":"","sources":["../src/search-stages.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,EAEL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,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;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,aAAa,CAAC,aAAa,CAAC,EAAE,CAAC;IAC1E;;;;;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,CA6E9B;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"}
|
package/dist/search-stages.js
CHANGED
|
@@ -27,15 +27,25 @@ export function searchStages(options) {
|
|
|
27
27
|
throw new Error(`Search type “${type.searchType.name}” (class ${type.searchType.class}) is not in the schema; searchStages projects only types the schema declares.`);
|
|
28
28
|
}
|
|
29
29
|
const { rootVariable } = type;
|
|
30
|
+
if (type.readers !== undefined && type.transform !== undefined) {
|
|
31
|
+
// Which of the caller’s readers the transform belongs to is knowable only
|
|
32
|
+
// to that caller, so guessing (all of them? the first?) would be a silent
|
|
33
|
+
// wrong answer. Attach it as an AttachedReader instead.
|
|
34
|
+
throw new Error(`Search type “${searchType.name}”: “transform” attaches to the default reader, so it cannot be combined with “readers” – attach the transform to your own reader instead ({ reader, transform }).`);
|
|
35
|
+
}
|
|
30
36
|
// Default to the Extraction CONSTRUCT generated from the schema, its subject
|
|
31
37
|
// left free for the batch’s VALUES injection. The reader and the projection
|
|
32
|
-
// then agree by construction: both key off the same IR Aliases
|
|
38
|
+
// then agree by construction: both key off the same IR Aliases – which is
|
|
39
|
+
// also why an attached transform never has to restate the subject variable.
|
|
33
40
|
const readers = type.readers ??
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
{
|
|
42
|
+
reader: new SparqlConstructReader({
|
|
43
|
+
query: extractionQueryString(searchType, schema, {
|
|
44
|
+
subjectVariable: rootVariable,
|
|
45
|
+
}),
|
|
37
46
|
}),
|
|
38
|
-
|
|
47
|
+
transform: type.transform,
|
|
48
|
+
};
|
|
39
49
|
return new Stage({
|
|
40
50
|
name: searchType.name,
|
|
41
51
|
readers,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search-pipeline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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,14 +25,14 @@
|
|
|
25
25
|
"!**/*.tsbuildinfo"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@lde/search": "^0.
|
|
28
|
+
"@lde/search": "^0.18.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.
|
|
35
|
+
"@lde/search-typesense": "^0.21.0",
|
|
36
36
|
"@rdfjs/types": "^2.0.1",
|
|
37
37
|
"n3": "^2.1.2",
|
|
38
38
|
"testcontainers": "^12.0.3",
|