@lde/search 0.4.0 → 0.5.0
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 +29 -2
- package/dist/frame-by-type.d.ts +39 -9
- package/dist/frame-by-type.d.ts.map +1 -1
- package/dist/frame-by-type.js +59 -37
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/project.d.ts +22 -4
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +17 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -150,8 +150,14 @@ const DATASET = defineSearchType({
|
|
|
150
150
|
],
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
-
for await (const document of projectGraph(
|
|
154
|
-
|
|
153
|
+
for await (const { searchType, document } of projectGraph(
|
|
154
|
+
quads,
|
|
155
|
+
searchSchema(DATASET),
|
|
156
|
+
)) {
|
|
157
|
+
// one flat search document per matching subject, streamed and tagged with the
|
|
158
|
+
// SearchType it was framed from — so a multi-collection writer can route each
|
|
159
|
+
// document to the collection for its type. A single-collection consumer just
|
|
160
|
+
// reads `document`.
|
|
155
161
|
}
|
|
156
162
|
```
|
|
157
163
|
|
|
@@ -218,6 +224,27 @@ triples are collapsed first, because some SPARQL engines (e.g. QLever) do not
|
|
|
218
224
|
deduplicate `CONSTRUCT` output. The IR carries no `@context`, so a `derive` function
|
|
219
225
|
reading it sees full predicate IRIs with language tags preserved.
|
|
220
226
|
|
|
227
|
+
Each yielded value is a `{ searchType, document }` pair: the whole schema
|
|
228
|
+
projects into **one** mixed stream, and every document is tagged with the
|
|
229
|
+
`SearchType` it was framed from. That tag is what lets the write side fan the
|
|
230
|
+
stream out to per-type collections without re-deriving the type from the
|
|
231
|
+
document — see `@lde/search-pipeline`’s multi-collection writer.
|
|
232
|
+
|
|
233
|
+
`projectGraph` **consumes the quads once** – a single scan builds the subject
|
|
234
|
+
index every type frames off – and so accepts any `Iterable<Quad>`, not just a
|
|
235
|
+
materialized array. A caller merging several sources can pass a chained
|
|
236
|
+
generator instead of building a third full array at the projection peak:
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
projectGraph(
|
|
240
|
+
(function* () {
|
|
241
|
+
yield* registerQuads;
|
|
242
|
+
yield* dkgQuads;
|
|
243
|
+
})(),
|
|
244
|
+
schema,
|
|
245
|
+
);
|
|
246
|
+
```
|
|
247
|
+
|
|
221
248
|
## Locales
|
|
222
249
|
|
|
223
250
|
`locales` is the **single** list of locales a `text` field projects (`und` =
|
package/dist/frame-by-type.d.ts
CHANGED
|
@@ -2,15 +2,45 @@ import type { Quad } from '@rdfjs/types';
|
|
|
2
2
|
/** A framed JSON-LD node (full-IRI keys); the engine-agnostic search IR. */
|
|
3
3
|
export type FramedNode = Record<string, unknown>;
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* A one-pass index of a quad source: every subject’s (deduplicated) triples,
|
|
6
|
+
* plus the root subjects of each requested type in appearance order. Built by
|
|
7
|
+
* {@link buildSubjectIndex} from a single iteration of the source, so a caller
|
|
8
|
+
* can pass a chained generator (`function* () { yield* a; yield* b; }`) rather
|
|
9
|
+
* than materialize a merged array; a multi-type projection then frames every
|
|
10
|
+
* type off this one index instead of re-scanning per type.
|
|
11
|
+
*/
|
|
12
|
+
export interface SubjectIndex {
|
|
13
|
+
/** Each subject IRI → its own deduplicated triples, in first-seen order. */
|
|
14
|
+
readonly bySubject: ReadonlyMap<string, readonly Quad[]>;
|
|
15
|
+
/** Each requested root type IRI → its root subjects, in appearance order. */
|
|
16
|
+
readonly rootsByType: ReadonlyMap<string, readonly string[]>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Iterate `quads` once, grouping each subject’s triples and collecting the root
|
|
20
|
+
* subjects of every type in `rootTypes`. Duplicate triples are collapsed here
|
|
21
|
+
* because some SPARQL engines (e.g. QLever) do not deduplicate CONSTRUCT
|
|
22
|
+
* output. The source is consumed a single time, so it may be a one-shot
|
|
23
|
+
* iterable (a generator chaining several sources); the whole subject index is
|
|
24
|
+
* held, but never more than that plus one framed subgraph at a time
|
|
25
|
+
* downstream.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildSubjectIndex(quads: Iterable<Quad>, rootTypes: Iterable<string>): SubjectIndex;
|
|
28
|
+
/**
|
|
29
|
+
* Frame every root subject of `rootType` from a prebuilt {@link SubjectIndex}
|
|
30
|
+
* into one JSON-LD node – the reusable core behind {@link frameByType}. Each
|
|
6
31
|
* root subject’s own triples plus the one-hop nodes it references (e.g. nested
|
|
7
|
-
* publisher/distribution resources) are
|
|
8
|
-
*
|
|
9
|
-
* `jsonld.frame()` is ~O(N²).
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
32
|
+
* publisher/distribution resources) are framed one subject at a time, so beyond
|
|
33
|
+
* the shared subject index only a single subgraph is held (whole-graph
|
|
34
|
+
* `jsonld.frame()` is ~O(N²)). The frame carries no `@context`, so framed keys
|
|
35
|
+
* are full predicate IRIs.
|
|
36
|
+
*/
|
|
37
|
+
export declare function frameSubjects(index: SubjectIndex, rootType: string): AsyncIterable<FramedNode>;
|
|
38
|
+
/**
|
|
39
|
+
* Frame CONSTRUCT quads into one JSON-LD node per subject of `rootType`, for a
|
|
40
|
+
* single root type. Consumes `quads` once (see {@link buildSubjectIndex}), so
|
|
41
|
+
* it accepts any `Iterable` – a materialized array or a chained generator. For
|
|
42
|
+
* a multi-type schema, {@link buildSubjectIndex} + {@link frameSubjects} share
|
|
43
|
+
* one index across types rather than re-scanning per type.
|
|
14
44
|
*/
|
|
15
|
-
export declare function frameByType(quads:
|
|
45
|
+
export declare function frameByType(quads: Iterable<Quad>, rootType: string): AsyncIterable<FramedNode>;
|
|
16
46
|
//# sourceMappingURL=frame-by-type.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-by-type.d.ts","sourceRoot":"","sources":["../src/frame-by-type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAMzC,4EAA4E;AAC5E,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAIjD
|
|
1
|
+
{"version":3,"file":"frame-by-type.d.ts","sourceRoot":"","sources":["../src/frame-by-type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAMzC,4EAA4E;AAC5E,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAIjD;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC;IACzD,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;CAC9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC1B,YAAY,CA8Bd;AAED;;;;;;;;GAQG;AACH,wBAAuB,aAAa,CAClC,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM,GACf,aAAa,CAAC,UAAU,CAAC,CA+B3B;AAED;;;;;;GAMG;AACH,wBAAuB,WAAW,CAChC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,QAAQ,EAAE,MAAM,GACf,aAAa,CAAC,UAAU,CAAC,CAE3B"}
|
package/dist/frame-by-type.js
CHANGED
|
@@ -3,40 +3,20 @@ import { rdf } from '@tpluscode/rdf-ns-builders';
|
|
|
3
3
|
const RDF_TYPE = rdf.type.value;
|
|
4
4
|
const FRAME_OPTIONS = { omitGraph: false, embed: '@always' };
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* type, keeping the framing domain-agnostic; the frame carries no `@context`, so
|
|
14
|
-
* framed keys are full predicate IRIs.
|
|
6
|
+
* Iterate `quads` once, grouping each subject’s triples and collecting the root
|
|
7
|
+
* subjects of every type in `rootTypes`. Duplicate triples are collapsed here
|
|
8
|
+
* because some SPARQL engines (e.g. QLever) do not deduplicate CONSTRUCT
|
|
9
|
+
* output. The source is consumed a single time, so it may be a one-shot
|
|
10
|
+
* iterable (a generator chaining several sources); the whole subject index is
|
|
11
|
+
* held, but never more than that plus one framed subgraph at a time
|
|
12
|
+
* downstream.
|
|
15
13
|
*/
|
|
16
|
-
export
|
|
17
|
-
for (const { rootIri, subgraph } of groupByRoot(quads, rootType)) {
|
|
18
|
-
const expanded = await jsonld.fromRDF(subgraph);
|
|
19
|
-
// Frame for THIS specific root subject by `@id`, not just by root type. A
|
|
20
|
-
// one-hop reference can itself be of `rootType` (e.g. a terminology source
|
|
21
|
-
// that is also a separately registered dataset), so framing by type alone
|
|
22
|
-
// returns several root nodes and `[0]` could be the referenced one — which
|
|
23
|
-
// would emit it twice and drop this subject entirely.
|
|
24
|
-
const framed = await jsonld.frame(expanded, { '@id': rootIri }, FRAME_OPTIONS);
|
|
25
|
-
const node = framed['@graph']?.[0];
|
|
26
|
-
if (node !== undefined) {
|
|
27
|
-
yield node;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Yield one self-contained quad subgraph per root subject – its own (deduped)
|
|
33
|
-
* triples plus the triples of the one-hop IRI or blank nodes it references –
|
|
34
|
-
* lazily, so only the subject index and the current subgraph are held at once
|
|
35
|
-
* (never the whole materialized list of subgraphs).
|
|
36
|
-
*/
|
|
37
|
-
function* groupByRoot(quads, rootType) {
|
|
14
|
+
export function buildSubjectIndex(quads, rootTypes) {
|
|
38
15
|
const bySubject = new Map();
|
|
39
|
-
const
|
|
16
|
+
const rootsByType = new Map();
|
|
17
|
+
for (const type of rootTypes) {
|
|
18
|
+
rootsByType.set(type, []);
|
|
19
|
+
}
|
|
40
20
|
const seen = new Set();
|
|
41
21
|
for (const quad of quads) {
|
|
42
22
|
const key = `${quad.subject.value} ${quad.predicate.value} ${quad.object.value} ${quad.object.termType === 'Literal' ? quad.object.language || quad.object.datatype.value : ''}`;
|
|
@@ -52,16 +32,58 @@ function* groupByRoot(quads, rootType) {
|
|
|
52
32
|
else {
|
|
53
33
|
owned.push(quad);
|
|
54
34
|
}
|
|
55
|
-
|
|
56
|
-
|
|
35
|
+
// `rootsByType` is keyed by exactly the requested root types, so the lookup
|
|
36
|
+
// doubles as the membership test: a hit means this subject is a root.
|
|
37
|
+
if (quad.predicate.value === RDF_TYPE) {
|
|
38
|
+
const roots = rootsByType.get(quad.object.value);
|
|
39
|
+
if (roots !== undefined) {
|
|
40
|
+
roots.push(subject);
|
|
41
|
+
}
|
|
57
42
|
}
|
|
58
43
|
}
|
|
59
|
-
|
|
60
|
-
|
|
44
|
+
return { bySubject, rootsByType };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Frame every root subject of `rootType` from a prebuilt {@link SubjectIndex}
|
|
48
|
+
* into one JSON-LD node – the reusable core behind {@link frameByType}. Each
|
|
49
|
+
* root subject’s own triples plus the one-hop nodes it references (e.g. nested
|
|
50
|
+
* publisher/distribution resources) are framed one subject at a time, so beyond
|
|
51
|
+
* the shared subject index only a single subgraph is held (whole-graph
|
|
52
|
+
* `jsonld.frame()` is ~O(N²)). The frame carries no `@context`, so framed keys
|
|
53
|
+
* are full predicate IRIs.
|
|
54
|
+
*/
|
|
55
|
+
export async function* frameSubjects(index, rootType) {
|
|
56
|
+
const { bySubject } = index;
|
|
57
|
+
// A type the index was not built for has no roots, so it frames nothing; the
|
|
58
|
+
// `projectType`/`projectGraph` callers only pass types they registered.
|
|
59
|
+
const roots = index.rootsByType.get(rootType) ?? [];
|
|
60
|
+
for (const rootIri of roots) {
|
|
61
|
+
const owned = bySubject.get(rootIri) ?? [];
|
|
61
62
|
const referenced = owned
|
|
62
63
|
.filter((quad) => quad.object.termType === 'NamedNode' ||
|
|
63
64
|
quad.object.termType === 'BlankNode')
|
|
64
65
|
.flatMap((quad) => bySubject.get(quad.object.value) ?? []);
|
|
65
|
-
|
|
66
|
+
const subgraph = [...owned, ...referenced];
|
|
67
|
+
const expanded = await jsonld.fromRDF(subgraph);
|
|
68
|
+
// Frame for THIS specific root subject by `@id`, not just by root type. A
|
|
69
|
+
// one-hop reference can itself be of `rootType` (e.g. a terminology source
|
|
70
|
+
// that is also a separately registered dataset), so framing by type alone
|
|
71
|
+
// returns several root nodes and `[0]` could be the referenced one — which
|
|
72
|
+
// would emit it twice and drop this subject entirely.
|
|
73
|
+
const framed = await jsonld.frame(expanded, { '@id': rootIri }, FRAME_OPTIONS);
|
|
74
|
+
const node = framed['@graph']?.[0];
|
|
75
|
+
if (node !== undefined) {
|
|
76
|
+
yield node;
|
|
77
|
+
}
|
|
66
78
|
}
|
|
67
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Frame CONSTRUCT quads into one JSON-LD node per subject of `rootType`, for a
|
|
82
|
+
* single root type. Consumes `quads` once (see {@link buildSubjectIndex}), so
|
|
83
|
+
* it accepts any `Iterable` – a materialized array or a chained generator. For
|
|
84
|
+
* a multi-type schema, {@link buildSubjectIndex} + {@link frameSubjects} share
|
|
85
|
+
* one index across types rather than re-scanning per type.
|
|
86
|
+
*/
|
|
87
|
+
export async function* frameByType(quads, rootType) {
|
|
88
|
+
yield* frameSubjects(buildSubjectIndex(quads, [rootType]), rootType);
|
|
89
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { projectGraph, irisOf, literalsOf, firstLiteralOf } from './project.js';
|
|
2
|
-
export type { SearchDocument } from './project.js';
|
|
2
|
+
export type { SearchDocument, TypedSearchDocument } from './project.js';
|
|
3
3
|
export { defineSearchType, searchSchema, validateSearchType, assertValidSearchType, } from './schema.js';
|
|
4
4
|
export type { FieldKind, SearchField, SearchFieldBase, TextField, KeywordField, ReferenceField, NumericField, BooleanField, SearchType, SearchTypeIssue, SearchSchema, FacetRange, } from './schema.js';
|
|
5
5
|
export type { SearchQuery, Filter, Sort } from './query.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAChF,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAChF,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAKxE,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,SAAS,EACT,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,eAAe,EACf,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC;AAIrB,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAI5D,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,WAAW,EACX,QAAQ,EACR,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/project.d.ts
CHANGED
|
@@ -5,6 +5,16 @@ import { type SearchSchema, type SearchType } from './schema.js';
|
|
|
5
5
|
export type SearchDocument = {
|
|
6
6
|
id: string;
|
|
7
7
|
} & Record<string, unknown>;
|
|
8
|
+
/**
|
|
9
|
+
* A projected document tagged with the {@link SearchType} it was projected
|
|
10
|
+
* from — one element of {@link projectGraph}’s mixed, whole-schema stream. The
|
|
11
|
+
* tag is what lets a multi-collection writer route each document to the
|
|
12
|
+
* collection for its type without re-deriving the type from the document.
|
|
13
|
+
*/
|
|
14
|
+
export interface TypedSearchDocument {
|
|
15
|
+
readonly searchType: SearchType;
|
|
16
|
+
readonly document: SearchDocument;
|
|
17
|
+
}
|
|
8
18
|
/**
|
|
9
19
|
* Project one framed JSON-LD node into a flat search document: apply each field
|
|
10
20
|
* of the type in declaration order. A field with a `derive` function computes
|
|
@@ -16,11 +26,19 @@ export type SearchDocument = {
|
|
|
16
26
|
export declare function projectDocument(node: FramedNode, searchType: SearchType): SearchDocument;
|
|
17
27
|
/**
|
|
18
28
|
* Frame `quads` for every root type in the schema and project each node with its
|
|
19
|
-
* type’s declaration — the multi-shape pipeline.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
29
|
+
* type’s declaration — the multi-shape pipeline. Yields each document tagged
|
|
30
|
+
* with its {@link SearchType} ({@link TypedSearchDocument}), so a downstream
|
|
31
|
+
* multi-collection writer can route it to that type’s collection; a
|
|
32
|
+
* single-collection consumer just reads `.document`. Streams one document at a
|
|
33
|
+
* time so memory stays flat. The IR maps to a declaration by type, so adding a
|
|
34
|
+
* shape is adding a `SearchType` to the schema (no engine change).
|
|
35
|
+
*
|
|
36
|
+
* Consumes `quads` once (a single scan builds the shared subject index that
|
|
37
|
+
* every type frames off), so it accepts any `Iterable` – a materialized array or
|
|
38
|
+
* a chained generator merging several sources (`function* () { yield* a; yield* b; }`)
|
|
39
|
+
* with no intermediate copy at the projection peak.
|
|
22
40
|
*/
|
|
23
|
-
export declare function projectGraph(quads:
|
|
41
|
+
export declare function projectGraph(quads: Iterable<Quad>, schema: SearchSchema): AsyncIterable<TypedSearchDocument>;
|
|
24
42
|
export declare function literalsOf(node: FramedNode, path: string): string[];
|
|
25
43
|
export declare function firstLiteralOf(node: FramedNode, path: string): string | undefined;
|
|
26
44
|
export declare function irisOf(node: FramedNode, path: string): string[];
|
package/dist/project.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAML,KAAK,YAAY,EACjB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAErB,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,UAAU,GACrB,cAAc,CAYhB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAuB,YAAY,CACjC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,MAAM,EAAE,YAAY,GACnB,aAAa,CAAC,mBAAmB,CAAC,CAWpC;AAsID,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAInE;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAI/D"}
|
package/dist/project.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fold } from '@lde/text-normalization';
|
|
2
|
-
import {
|
|
2
|
+
import { buildSubjectIndex, frameSubjects, } from './frame-by-type.js';
|
|
3
3
|
import { isoToUnixSeconds, physicalFields, } from './schema.js';
|
|
4
4
|
/**
|
|
5
5
|
* Project one framed JSON-LD node into a flat search document: apply each field
|
|
@@ -22,14 +22,24 @@ export function projectDocument(node, searchType) {
|
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Frame `quads` for every root type in the schema and project each node with its
|
|
25
|
-
* type’s declaration — the multi-shape pipeline.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* type’s declaration — the multi-shape pipeline. Yields each document tagged
|
|
26
|
+
* with its {@link SearchType} ({@link TypedSearchDocument}), so a downstream
|
|
27
|
+
* multi-collection writer can route it to that type’s collection; a
|
|
28
|
+
* single-collection consumer just reads `.document`. Streams one document at a
|
|
29
|
+
* time so memory stays flat. The IR maps to a declaration by type, so adding a
|
|
30
|
+
* shape is adding a `SearchType` to the schema (no engine change).
|
|
31
|
+
*
|
|
32
|
+
* Consumes `quads` once (a single scan builds the shared subject index that
|
|
33
|
+
* every type frames off), so it accepts any `Iterable` – a materialized array or
|
|
34
|
+
* a chained generator merging several sources (`function* () { yield* a; yield* b; }`)
|
|
35
|
+
* with no intermediate copy at the projection peak.
|
|
28
36
|
*/
|
|
29
37
|
export async function* projectGraph(quads, schema) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
const types = [...schema.values()];
|
|
39
|
+
const index = buildSubjectIndex(quads, types.map((searchType) => searchType.type));
|
|
40
|
+
for (const searchType of types) {
|
|
41
|
+
for await (const node of frameSubjects(index, searchType.type)) {
|
|
42
|
+
yield { searchType, document: projectDocument(node, searchType) };
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
45
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Engine- and domain-agnostic core for RDF-backed search: a unified declarative field model (SearchField/SearchType/SearchSchema), a neutral query IR, the SearchEngine port with logical result types, and a streaming CONSTRUCT-to-document projection. Bakes in no engine, protocol, or domain.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "git+https://github.com/ldelements/lde.git",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/jsonld": "^1.5.15",
|
|
48
|
-
"n3": "^2.1.
|
|
48
|
+
"n3": "^2.1.1"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"vitest": "^4.0.0"
|