@lde/search-pipeline 0.12.1 → 0.12.2
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
CHANGED
|
@@ -34,6 +34,62 @@ The division of labour ([ADR 6](../../docs/decisions/0006-make-the-writer-transa
|
|
|
34
34
|
|
|
35
35
|
So a search pipeline is **one terminal, N stages**: `new Pipeline<TypedSearchDocument>({ datasetSelector, stages: searchStages(...), writers: searchIndexWriter(...) })`.
|
|
36
36
|
|
|
37
|
+
## Quick start: `searchIndexerPipeline`
|
|
38
|
+
|
|
39
|
+
For the common **object grain** – index every IRI-identified instance of each
|
|
40
|
+
root type’s source class – `searchIndexerPipeline` wires that whole composition
|
|
41
|
+
and returns the ready-to-run `Pipeline`. It generates one stage per root type
|
|
42
|
+
in the schema, each selecting the type’s non-blank roots by class
|
|
43
|
+
(`selectByClass`; a blank node has no stable document key, so it can never
|
|
44
|
+
become a document), extracting with the schema-generated CONSTRUCT, and routing
|
|
45
|
+
every document to the engine writer for its type’s collection. The consumer
|
|
46
|
+
supplies only the domain (the schema, which datasets) and the deployment shell
|
|
47
|
+
(the engine writer, the SPARQL/import adapter):
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { searchIndexerPipeline } from '@lde/search-pipeline';
|
|
51
|
+
import {
|
|
52
|
+
FileProvenanceStore,
|
|
53
|
+
ImportResolver,
|
|
54
|
+
SparqlDistributionResolver,
|
|
55
|
+
} from '@lde/pipeline';
|
|
56
|
+
import { ConsoleReporter } from '@lde/pipeline-console-reporter';
|
|
57
|
+
import { createQlever } from '@lde/sparql-qlever';
|
|
58
|
+
import { InPlaceRebuild } from '@lde/search-typesense';
|
|
59
|
+
|
|
60
|
+
const pipeline = searchIndexerPipeline({
|
|
61
|
+
schema,
|
|
62
|
+
datasets,
|
|
63
|
+
// Import each dataset’s data dump into a local QLever and query that.
|
|
64
|
+
distributionResolver: new ImportResolver(new SparqlDistributionResolver(), {
|
|
65
|
+
...createQlever({ mode: 'docker', image: 'adfreiburg/qlever:latest' }),
|
|
66
|
+
strategy: 'import',
|
|
67
|
+
}),
|
|
68
|
+
writerFor: (searchType) => new InPlaceRebuild(typesenseClient, searchType),
|
|
69
|
+
// Optional: skip datasets whose source and pipeline version are unchanged.
|
|
70
|
+
provenanceStore: new FileProvenanceStore({ path: 'data/provenance.json' }),
|
|
71
|
+
pipelineVersion: '1',
|
|
72
|
+
reporter: new ConsoleReporter(),
|
|
73
|
+
});
|
|
74
|
+
await pipeline.run();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Each role in that wiring maps to a package:
|
|
78
|
+
|
|
79
|
+
| Role | Package |
|
|
80
|
+
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
81
|
+
| Dataset selection | [`@lde/pipeline`](../pipeline) – `Dataset[]` directly, or a `DatasetSelector` such as `RegistrySelector` |
|
|
82
|
+
| Distribution resolution and import | [`@lde/pipeline`](../pipeline) `ImportResolver` + [`@lde/sparql-qlever`](../sparql-qlever) `createQlever` (the concrete [`@lde/sparql-server`](../sparql-server)/[`@lde/sparql-importer`](../sparql-importer) adapter) |
|
|
83
|
+
| Root selection, extraction, projection | this package (`searchStages` over [`@lde/search`](../search)) |
|
|
84
|
+
| Engine writer, one collection per type | [`@lde/search-typesense`](../search-typesense) – `InPlaceRebuild` or `BlueGreenRebuild` |
|
|
85
|
+
| Provenance (skip-unchanged) | [`@lde/pipeline`](../pipeline) – `FileProvenanceStore`, or a triplestore-backed store |
|
|
86
|
+
| Reporting | [`@lde/pipeline-console-reporter`](../pipeline-console-reporter) `ConsoleReporter` |
|
|
87
|
+
|
|
88
|
+
A deployment that needs more – a bespoke root selector (where the entry point
|
|
89
|
+
is a domain fact, not a class), per-stage tuning, non-SPARQL readers, or
|
|
90
|
+
quad-level plugins – composes the parts directly, as below; the convenience
|
|
91
|
+
owns no capability of its own.
|
|
92
|
+
|
|
37
93
|
## Usage
|
|
38
94
|
|
|
39
95
|
```typescript
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { searchIndexerPipeline } from './search-indexer-pipeline.js';
|
|
2
|
+
export type { SearchIndexerPipelineOptions } from './search-indexer-pipeline.js';
|
|
1
3
|
export { searchStages, selectByClass } from './search-stages.js';
|
|
2
4
|
export type { SearchStagesOptions, SearchStageType } from './search-stages.js';
|
|
3
5
|
export { extractionQuery, extractionQueryString } from './extraction.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,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,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"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { searchIndexerPipeline } from './search-indexer-pipeline.js';
|
|
1
2
|
export { searchStages, selectByClass } from './search-stages.js';
|
|
2
3
|
export { extractionQuery, extractionQueryString } from './extraction.js';
|
|
3
4
|
export { searchIndexWriter } from './search-index-writer.js';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Dataset } from '@lde/dataset';
|
|
2
|
+
import { Pipeline, type DatasetSelector, type DistributionResolver, type ProgressReporter, type ProvenanceStore, type Writer } from '@lde/pipeline';
|
|
3
|
+
import type { RootType, SearchDocument, SearchSchema } from '@lde/search';
|
|
4
|
+
import type { TypedSearchDocument } from './typed-search-document.js';
|
|
5
|
+
/** Options for {@link searchIndexerPipeline}. */
|
|
6
|
+
export interface SearchIndexerPipelineOptions {
|
|
7
|
+
/**
|
|
8
|
+
* The declarative schema driving the indexer: one stage and one engine
|
|
9
|
+
* collection per {@link RootType} in it. Reference Types are absent from
|
|
10
|
+
* `schema.values()`, so none ever earns a stage or a collection.
|
|
11
|
+
*/
|
|
12
|
+
schema: SearchSchema;
|
|
13
|
+
/**
|
|
14
|
+
* Which datasets to index – the deployment’s domain fact. Pass the
|
|
15
|
+
* {@link Dataset}s directly, or a {@link DatasetSelector} (e.g. a
|
|
16
|
+
* `RegistrySelector`) when selection is dynamic.
|
|
17
|
+
*/
|
|
18
|
+
datasets: Dataset[] | DatasetSelector;
|
|
19
|
+
/**
|
|
20
|
+
* How each dataset’s data becomes queryable – the deployment’s engine
|
|
21
|
+
* choice. Typically an `ImportResolver` wrapping a
|
|
22
|
+
* `SparqlDistributionResolver` with a `@lde/sparql-qlever` `createQlever`
|
|
23
|
+
* import path, so data-dump distributions are imported into a local
|
|
24
|
+
* endpoint. Defaults to the {@link Pipeline} default (a bare
|
|
25
|
+
* `SparqlDistributionResolver`), which serves only datasets that publish a
|
|
26
|
+
* live SPARQL endpoint.
|
|
27
|
+
*/
|
|
28
|
+
distributionResolver?: DistributionResolver;
|
|
29
|
+
/**
|
|
30
|
+
* The engine writer that owns a given root type’s collection – e.g. a
|
|
31
|
+
* `@lde/search-typesense` `InPlaceRebuild` or `BlueGreenRebuild` bound to
|
|
32
|
+
* that type. Called once per {@link RootType} in the schema.
|
|
33
|
+
*/
|
|
34
|
+
writerFor: (searchType: RootType) => Writer<SearchDocument>;
|
|
35
|
+
/**
|
|
36
|
+
* Optional per-dataset processing memory: skip a dataset whose source
|
|
37
|
+
* fingerprint and {@link pipelineVersion} both match the stored record.
|
|
38
|
+
* Requires {@link pipelineVersion}.
|
|
39
|
+
*/
|
|
40
|
+
provenanceStore?: ProvenanceStore;
|
|
41
|
+
/**
|
|
42
|
+
* Opaque, consumer-declared version of the indexer’s output-affecting logic
|
|
43
|
+
* (schema, projection). Required when {@link provenanceStore} is set – a
|
|
44
|
+
* skip-enabled pipeline with no version would silently freeze.
|
|
45
|
+
*/
|
|
46
|
+
pipelineVersion?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Observer(s) of pipeline lifecycle events, e.g. a
|
|
49
|
+
* `@lde/pipeline-console-reporter` `ConsoleReporter`.
|
|
50
|
+
*/
|
|
51
|
+
reporter?: ProgressReporter | readonly ProgressReporter[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Wire the common **object-grain** search indexer and return the ready-to-run
|
|
55
|
+
* {@link Pipeline}: one projecting stage per {@link RootType} in the schema,
|
|
56
|
+
* each selecting the type’s roots by its source class with blank-node subjects
|
|
57
|
+
* excluded ({@link selectByClass} – a blank node has no stable document key),
|
|
58
|
+
* extracting with the schema-generated CONSTRUCT, and projecting inside the
|
|
59
|
+
* batch; plus the single {@link searchIndexWriter} terminal routing each
|
|
60
|
+
* document to the engine writer for its type’s collection.
|
|
61
|
+
*
|
|
62
|
+
* The consumer supplies only the domain and the deployment shell: the schema,
|
|
63
|
+
* which datasets, the engine writer per type, and (optionally) the
|
|
64
|
+
* SPARQL/import adapter, provenance and reporting.
|
|
65
|
+
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* const pipeline = searchIndexerPipeline({
|
|
68
|
+
* schema,
|
|
69
|
+
* datasets,
|
|
70
|
+
* distributionResolver: new ImportResolver(new SparqlDistributionResolver(), {
|
|
71
|
+
* ...createQlever({ mode: 'docker', image: 'adfreiburg/qlever:latest' }),
|
|
72
|
+
* strategy: 'import',
|
|
73
|
+
* }),
|
|
74
|
+
* writerFor: (searchType) => new InPlaceRebuild(typesenseClient, searchType),
|
|
75
|
+
* });
|
|
76
|
+
* await pipeline.run();
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* A deployment that needs more – a bespoke root selector (the entry point is a
|
|
80
|
+
* domain fact, not a class), per-stage tuning (`batchSize`, `maxConcurrency`),
|
|
81
|
+
* non-SPARQL readers, or quad-level plugins – composes {@link searchStages},
|
|
82
|
+
* {@link searchIndexWriter} and {@link Pipeline} directly; this convenience
|
|
83
|
+
* owns no capability of its own.
|
|
84
|
+
*/
|
|
85
|
+
export declare function searchIndexerPipeline(options: SearchIndexerPipelineOptions): Pipeline<TypedSearchDocument>;
|
|
86
|
+
//# sourceMappingURL=search-indexer-pipeline.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ManualDatasetSelection, Pipeline, } from '@lde/pipeline';
|
|
2
|
+
import { searchIndexWriter } from './search-index-writer.js';
|
|
3
|
+
import { searchStages, selectByClass } from './search-stages.js';
|
|
4
|
+
/**
|
|
5
|
+
* Wire the common **object-grain** search indexer and return the ready-to-run
|
|
6
|
+
* {@link Pipeline}: one projecting stage per {@link RootType} in the schema,
|
|
7
|
+
* each selecting the type’s roots by its source class with blank-node subjects
|
|
8
|
+
* excluded ({@link selectByClass} – a blank node has no stable document key),
|
|
9
|
+
* extracting with the schema-generated CONSTRUCT, and projecting inside the
|
|
10
|
+
* batch; plus the single {@link searchIndexWriter} terminal routing each
|
|
11
|
+
* document to the engine writer for its type’s collection.
|
|
12
|
+
*
|
|
13
|
+
* The consumer supplies only the domain and the deployment shell: the schema,
|
|
14
|
+
* which datasets, the engine writer per type, and (optionally) the
|
|
15
|
+
* SPARQL/import adapter, provenance and reporting.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* const pipeline = searchIndexerPipeline({
|
|
19
|
+
* schema,
|
|
20
|
+
* datasets,
|
|
21
|
+
* distributionResolver: new ImportResolver(new SparqlDistributionResolver(), {
|
|
22
|
+
* ...createQlever({ mode: 'docker', image: 'adfreiburg/qlever:latest' }),
|
|
23
|
+
* strategy: 'import',
|
|
24
|
+
* }),
|
|
25
|
+
* writerFor: (searchType) => new InPlaceRebuild(typesenseClient, searchType),
|
|
26
|
+
* });
|
|
27
|
+
* await pipeline.run();
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* A deployment that needs more – a bespoke root selector (the entry point is a
|
|
31
|
+
* domain fact, not a class), per-stage tuning (`batchSize`, `maxConcurrency`),
|
|
32
|
+
* non-SPARQL readers, or quad-level plugins – composes {@link searchStages},
|
|
33
|
+
* {@link searchIndexWriter} and {@link Pipeline} directly; this convenience
|
|
34
|
+
* owns no capability of its own.
|
|
35
|
+
*/
|
|
36
|
+
export function searchIndexerPipeline(options) {
|
|
37
|
+
const { schema, datasets } = options;
|
|
38
|
+
return new Pipeline({
|
|
39
|
+
datasetSelector: Array.isArray(datasets)
|
|
40
|
+
? new ManualDatasetSelection(datasets)
|
|
41
|
+
: datasets,
|
|
42
|
+
distributionResolver: options.distributionResolver,
|
|
43
|
+
stages: searchStages({
|
|
44
|
+
schema,
|
|
45
|
+
types: [...schema.values()].map((searchType) => ({
|
|
46
|
+
searchType,
|
|
47
|
+
rootVariable: 'root',
|
|
48
|
+
itemSelector: selectByClass(searchType),
|
|
49
|
+
})),
|
|
50
|
+
}),
|
|
51
|
+
writers: searchIndexWriter({ schema, writerFor: options.writerFor }),
|
|
52
|
+
provenanceStore: options.provenanceStore,
|
|
53
|
+
pipelineVersion: options.pipelineVersion,
|
|
54
|
+
reporter: options.reporter,
|
|
55
|
+
});
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search-pipeline",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
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",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"tslib": "^2.3.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@lde/search-typesense": "^0.15.
|
|
35
|
+
"@lde/search-typesense": "^0.15.2",
|
|
36
36
|
"@rdfjs/types": "^2.0.1",
|
|
37
37
|
"n3": "^2.1.1",
|
|
38
38
|
"testcontainers": "^12.0.3",
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@lde/dataset": "^0.7.8",
|
|
43
|
-
"@lde/pipeline": "^0.35.
|
|
43
|
+
"@lde/pipeline": "^0.35.1"
|
|
44
44
|
}
|
|
45
45
|
}
|