@lde/search 0.3.1 → 0.4.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/README.md +22 -1
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +1 -1
- package/dist/engine.d.ts +1 -1
- 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/project.d.ts +6 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +10 -3
- package/dist/schema.d.ts +19 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +45 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -203,6 +203,12 @@ A `reference` carries `labelOnly` today (id + display label); the `idOnly` and
|
|
|
203
203
|
day one so that `inline` can later **add** fields to a reference type without
|
|
204
204
|
breaking clients.
|
|
205
205
|
|
|
206
|
+
A reference resolves its label from a **label source**: `labelSource` names
|
|
207
|
+
the `SearchType` whose collection holds the referenced entities. The named
|
|
208
|
+
type must declare an `output`, `searchable` text field called `label` –
|
|
209
|
+
`searchSchema` validates this schema-wide, so a dangling or unsuitable label
|
|
210
|
+
source fails at startup. A reference without a `labelSource` stays id-only.
|
|
211
|
+
|
|
206
212
|
## Projection
|
|
207
213
|
|
|
208
214
|
`projectGraph` is fully streaming: subjects are grouped and framed one at a time
|
|
@@ -212,6 +218,21 @@ triples are collapsed first, because some SPARQL engines (e.g. QLever) do not
|
|
|
212
218
|
deduplicate `CONSTRUCT` output. The IR carries no `@context`, so a `derive` function
|
|
213
219
|
reading it sees full predicate IRIs with language tags preserved.
|
|
214
220
|
|
|
221
|
+
`projectGraph` **consumes the quads once** – a single scan builds the subject
|
|
222
|
+
index every type frames off – and so accepts any `Iterable<Quad>`, not just a
|
|
223
|
+
materialized array. A caller merging several sources can pass a chained
|
|
224
|
+
generator instead of building a third full array at the projection peak:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
projectGraph(
|
|
228
|
+
(function* () {
|
|
229
|
+
yield* registerQuads;
|
|
230
|
+
yield* dkgQuads;
|
|
231
|
+
})(),
|
|
232
|
+
schema,
|
|
233
|
+
);
|
|
234
|
+
```
|
|
235
|
+
|
|
215
236
|
## Locales
|
|
216
237
|
|
|
217
238
|
`locales` is the **single** list of locales a `text` field projects (`und` =
|
|
@@ -286,7 +307,7 @@ await engine.search(OTHER_TYPE, query); // compile error: not in this schema
|
|
|
286
307
|
```
|
|
287
308
|
|
|
288
309
|
`searchFacets(type, queries)` is the port’s **batch entry point**: several
|
|
289
|
-
facet-only queries – e.g. a faceted
|
|
310
|
+
facet-only queries – e.g. a faceted listing’s skip-own-filter variants –
|
|
290
311
|
answered in one engine round-trip (Typesense: a single `multi_search`), one
|
|
291
312
|
outcome per query, positionally aligned – its facet map, or an in-place error,
|
|
292
313
|
so one failed query never discards its siblings’ facets. The same schema
|
package/dist/adapter.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* deployment author declaring a schema and running searches needs none of
|
|
7
7
|
* this; the main entry point carries that (small) authoring surface.
|
|
8
8
|
*/
|
|
9
|
-
export { assertTypeInSchema, physicalFields, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, referenceFields, fieldNamed, isRangeFacet, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
9
|
+
export { assertTypeInSchema, physicalFields, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, referenceFields, fieldNamed, labelFieldOf, isRangeFacet, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
10
10
|
export type { PhysicalFields } from './schema.js';
|
|
11
11
|
export { filterOperatorFor, filterOperator, validateQuery, assertValidQuery, pageForOffset, } from './query.js';
|
|
12
12
|
export type { FilterOperator, QueryIssue } from './query.js';
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/adapter.js
CHANGED
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
* deployment author declaring a schema and running searches needs none of
|
|
7
7
|
* this; the main entry point carries that (small) authoring surface.
|
|
8
8
|
*/
|
|
9
|
-
export { assertTypeInSchema, physicalFields, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, referenceFields, fieldNamed, isRangeFacet, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
9
|
+
export { assertTypeInSchema, physicalFields, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, referenceFields, fieldNamed, labelFieldOf, isRangeFacet, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
10
10
|
export { filterOperatorFor, filterOperator, validateQuery, assertValidQuery, pageForOffset, } from './query.js';
|
package/dist/engine.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export interface SearchEngine<Types extends readonly SearchType[] = readonly Sea
|
|
|
40
40
|
* in a single engine round-trip (where the engine supports one, e.g.
|
|
41
41
|
* Typesense `multi_search`), returning one {@link FacetsOutcome} per query,
|
|
42
42
|
* positionally aligned with `queries`. This is what keeps a faceted
|
|
43
|
-
*
|
|
43
|
+
* listing – one skip-own-filter query variant per filtered facet – from
|
|
44
44
|
* fanning out into per-facet engine calls. Only facet buckets come back: an
|
|
45
45
|
* engine answers every query in the batch facet-only (as if `limit: 0` and
|
|
46
46
|
* without ordering, whatever the query carries), so hits are never
|
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/project.d.ts
CHANGED
|
@@ -19,8 +19,13 @@ export declare function projectDocument(node: FramedNode, searchType: SearchType
|
|
|
19
19
|
* type’s declaration — the multi-shape pipeline. Streams one document at a time
|
|
20
20
|
* so memory stays flat. The IR maps to a declaration by type, so adding a shape
|
|
21
21
|
* is adding a `SearchType` to the schema (no engine change).
|
|
22
|
+
*
|
|
23
|
+
* Consumes `quads` once (a single scan builds the shared subject index that
|
|
24
|
+
* every type frames off), so it accepts any `Iterable` – a materialized array or
|
|
25
|
+
* a chained generator merging several sources (`function* () { yield* a; yield* b; }`)
|
|
26
|
+
* with no intermediate copy at the projection peak.
|
|
22
27
|
*/
|
|
23
|
-
export declare function projectGraph(quads:
|
|
28
|
+
export declare function projectGraph(quads: Iterable<Quad>, schema: SearchSchema): AsyncIterable<SearchDocument>;
|
|
24
29
|
export declare function literalsOf(node: FramedNode, path: string): string[];
|
|
25
30
|
export declare function firstLiteralOf(node: FramedNode, path: string): string | undefined;
|
|
26
31
|
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;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,UAAU,GACrB,cAAc,CAYhB;AAED;;;;;;;;;;GAUG;AACH,wBAAuB,YAAY,CACjC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,MAAM,EAAE,YAAY,GACnB,aAAa,CAAC,cAAc,CAAC,CAW/B;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
|
|
@@ -25,10 +25,17 @@ export function projectDocument(node, searchType) {
|
|
|
25
25
|
* type’s declaration — the multi-shape pipeline. Streams one document at a time
|
|
26
26
|
* so memory stays flat. The IR maps to a declaration by type, so adding a shape
|
|
27
27
|
* is adding a `SearchType` to the schema (no engine change).
|
|
28
|
+
*
|
|
29
|
+
* Consumes `quads` once (a single scan builds the shared subject index that
|
|
30
|
+
* every type frames off), so it accepts any `Iterable` – a materialized array or
|
|
31
|
+
* a chained generator merging several sources (`function* () { yield* a; yield* b; }`)
|
|
32
|
+
* with no intermediate copy at the projection peak.
|
|
28
33
|
*/
|
|
29
34
|
export async function* projectGraph(quads, schema) {
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
const types = [...schema.values()];
|
|
36
|
+
const index = buildSubjectIndex(quads, types.map((searchType) => searchType.type));
|
|
37
|
+
for (const searchType of types) {
|
|
38
|
+
for await (const node of frameSubjects(index, searchType.type)) {
|
|
32
39
|
yield projectDocument(node, searchType);
|
|
33
40
|
}
|
|
34
41
|
}
|
package/dist/schema.d.ts
CHANGED
|
@@ -125,6 +125,15 @@ export interface ReferenceField extends SearchFieldBase, Searchable {
|
|
|
125
125
|
/** Projection-time value transform. */
|
|
126
126
|
readonly transform?: (value: string) => string;
|
|
127
127
|
readonly facetRanges?: never;
|
|
128
|
+
/**
|
|
129
|
+
* The `name` of the {@link SearchType} whose collection resolves this
|
|
130
|
+
* reference’s labels – its ‘label source’. The named type must declare an
|
|
131
|
+
* `output`, `searchable` text field called `label` (validated by
|
|
132
|
+
* {@link searchSchema}), so an engine can both reconstruct the label and
|
|
133
|
+
* search it (typeahead). Omit for an id-only reference: no label
|
|
134
|
+
* resolution.
|
|
135
|
+
*/
|
|
136
|
+
readonly labelSource?: string;
|
|
128
137
|
/** The referenced entity’s shape and how much of it to carry. Required when
|
|
129
138
|
* the field is `output` (the API surfaces need the reference type name);
|
|
130
139
|
* optional for a facet- or filter-only reference. */
|
|
@@ -234,6 +243,15 @@ export interface SearchSchema<Types extends readonly SearchType[] = readonly Sea
|
|
|
234
243
|
* schema fails at startup, not per document at index time or per query.
|
|
235
244
|
*/
|
|
236
245
|
export declare function searchSchema<const Types extends readonly SearchType[]>(...types: Types): SearchSchema<Types>;
|
|
246
|
+
/**
|
|
247
|
+
* The text field a label source serves labels from – the ‘label’ convention
|
|
248
|
+
* in one place: an `output` (something to reconstruct a label from),
|
|
249
|
+
* `searchable` (something to type ahead against) text field called `label`.
|
|
250
|
+
* Returns `undefined` when the type declares no such field; a schema built by
|
|
251
|
+
* {@link searchSchema} guarantees it for every type named as a
|
|
252
|
+
* {@link ReferenceField.labelSource}.
|
|
253
|
+
*/
|
|
254
|
+
export declare function labelFieldOf(searchType: SearchType): TextField | undefined;
|
|
237
255
|
/**
|
|
238
256
|
* One structural problem {@link validateSearchType} found: a field declares a
|
|
239
257
|
* capability or property its `kind` cannot honour, or the declaration is
|
|
@@ -315,7 +333,7 @@ export declare function sortableFields(searchType: SearchType): readonly SearchF
|
|
|
315
333
|
/** Fields that appear in the API output type, in declaration order. */
|
|
316
334
|
export declare function outputFields(searchType: SearchType): readonly SearchField[];
|
|
317
335
|
/** Fields of kind `reference` (IRI-valued, label-resolved), in declaration order. */
|
|
318
|
-
export declare function referenceFields(searchType: SearchType): readonly
|
|
336
|
+
export declare function referenceFields(searchType: SearchType): readonly ReferenceField[];
|
|
319
337
|
/** Look up a field by its logical name. */
|
|
320
338
|
export declare function fieldNamed(searchType: SearchType, name: string): SearchField | undefined;
|
|
321
339
|
/**
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.js
CHANGED
|
@@ -51,9 +51,54 @@ export function searchSchema(...types) {
|
|
|
51
51
|
typeIris.add(searchType.type);
|
|
52
52
|
names.add(searchType.name);
|
|
53
53
|
}
|
|
54
|
+
assertResolvableLabelSources(types);
|
|
54
55
|
// The one blessed cast: only this validated constructor mints the brand.
|
|
55
56
|
return new Map(types.map((searchType) => [searchType.type, searchType]));
|
|
56
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* The text field a label source serves labels from – the ‘label’ convention
|
|
60
|
+
* in one place: an `output` (something to reconstruct a label from),
|
|
61
|
+
* `searchable` (something to type ahead against) text field called `label`.
|
|
62
|
+
* Returns `undefined` when the type declares no such field; a schema built by
|
|
63
|
+
* {@link searchSchema} guarantees it for every type named as a
|
|
64
|
+
* {@link ReferenceField.labelSource}.
|
|
65
|
+
*/
|
|
66
|
+
export function labelFieldOf(searchType) {
|
|
67
|
+
const field = fieldNamed(searchType, 'label');
|
|
68
|
+
return field !== undefined &&
|
|
69
|
+
field.kind === 'text' &&
|
|
70
|
+
field.output === true &&
|
|
71
|
+
field.searchable !== undefined
|
|
72
|
+
? field
|
|
73
|
+
: undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Every {@link ReferenceField.labelSource} must name a declared type that can
|
|
77
|
+
* actually serve labels ({@link labelFieldOf}). Checked schema-wide, because
|
|
78
|
+
* a single declaration cannot see its siblings.
|
|
79
|
+
*/
|
|
80
|
+
function assertResolvableLabelSources(types) {
|
|
81
|
+
const byName = new Map(types.map((searchType) => [searchType.name, searchType]));
|
|
82
|
+
for (const searchType of types) {
|
|
83
|
+
for (const field of searchType.fields) {
|
|
84
|
+
const labelSource = field
|
|
85
|
+
.labelSource;
|
|
86
|
+
if (labelSource === undefined) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (field.kind !== 'reference') {
|
|
90
|
+
throw new Error(`Field “${searchType.name}.${field.name}” declares a label source but is a ${field.kind} field; only reference fields resolve labels from a source.`);
|
|
91
|
+
}
|
|
92
|
+
const source = byName.get(labelSource);
|
|
93
|
+
if (source === undefined) {
|
|
94
|
+
throw new Error(`Reference “${searchType.name}.${field.name}” names unknown label source “${field.labelSource}”; declare a SearchType with that name.`);
|
|
95
|
+
}
|
|
96
|
+
if (labelFieldOf(source) === undefined) {
|
|
97
|
+
throw new Error(`Reference “${searchType.name}.${field.name}” uses label source “${field.labelSource}”, which must declare an output, searchable text field “label”.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
57
102
|
/** Kinds that can feed full-text search (project a folded search field). */
|
|
58
103
|
const SEARCHABLE_KINDS = ['text', 'keyword', 'reference'];
|
|
59
104
|
/** Kinds whose projection applies the {@link KeywordField.transform}. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
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"
|