@lde/search 0.15.0 → 0.16.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/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/project.d.ts +18 -3
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +38 -11
- package/dist/schema.d.ts +58 -7
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +61 -1
- package/package.json +2 -2
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, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, referenceFields, referenceTypeNamed, inlineFramingDepth, fieldNamed, ID_FIELD, labelFieldOf, isRangeFacet, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
9
|
+
export { assertTypeInSchema, physicalFields, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, referenceFields, referenceTypeNamed, inlineFramingDepth, fieldNamed, datasetField, ID_FIELD, labelFieldOf, isRangeFacet, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
10
10
|
export type { PhysicalFields } from './schema.js';
|
|
11
11
|
export { physicalNameTokens } from './physical-name.js';
|
|
12
12
|
export { filterOperatorFor, filterOperator, isUnsatisfiable, validateQuery, assertValidQuery, pageForOffset, } 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,OAAO,EACP,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,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,OAAO,EACP,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,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,6 +6,6 @@
|
|
|
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, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, referenceFields, referenceTypeNamed, inlineFramingDepth, fieldNamed, ID_FIELD, labelFieldOf, isRangeFacet, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
9
|
+
export { assertTypeInSchema, physicalFields, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, referenceFields, referenceTypeNamed, inlineFramingDepth, fieldNamed, datasetField, ID_FIELD, labelFieldOf, isRangeFacet, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
10
10
|
export { physicalNameTokens } from './physical-name.js';
|
|
11
11
|
export { filterOperatorFor, filterOperator, isUnsatisfiable, validateQuery, assertValidQuery, pageForOffset, } from './query.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { projectRoots } from './project.js';
|
|
2
|
-
export type { ProjectedNode, SearchDocument } from './project.js';
|
|
2
|
+
export type { ProjectedNode, ProjectionContext, SearchDocument, } from './project.js';
|
|
3
3
|
export { defineSearchType, searchSchema, validateSearchType, assertValidSearchType, ID_FIELD, } from './schema.js';
|
|
4
|
-
export type { FieldKind, SearchField, SearchFieldBase, TextField, KeywordField, ReferenceField, NumericField, BooleanField, SearchType, SearchTypeBase, RootType, ReferenceType, RootTypeOf, SearchTypeIssue, SearchSchema, FacetRange, } from './schema.js';
|
|
4
|
+
export type { FieldKind, SearchField, SearchFieldBase, TextField, KeywordField, ReferenceField, NumericField, BooleanField, SearchType, SearchTypeBase, RootType, ReferenceType, RootTypeOf, SearchTypeIssue, SearchSchema, FacetRange, ProjectionValue, } from './schema.js';
|
|
5
5
|
export type { SearchQuery, Filter, Sort } from './query.js';
|
|
6
6
|
export type { SearchEngine, SearchResult, SearchHit, ResultDocument, SearchValue, LocalizedValue, Reference, NestedDocument, FacetBucket, FacetMap, FacetsOutcome, FacetFieldsOf, OutputFieldsOf, } from './engine.js';
|
|
7
7
|
export type { FramedNode } from './frame-by-type.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,cAAc,CAAC;AAC5C,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,cAAc,GACf,MAAM,cAAc,CAAC;AAKtB,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,SAAS,EACT,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,cAAc,EACd,QAAQ,EACR,aAAa,EACb,UAAU,EACV,eAAe,EACf,YAAY,EACZ,UAAU,EACV,eAAe,GAChB,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,cAAc,EACd,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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Quad } from '@rdfjs/types';
|
|
2
2
|
import { type FramedNode } from './frame-by-type.js';
|
|
3
|
-
import { type RootType, type SearchSchema, type SearchType } from './schema.js';
|
|
3
|
+
import { type ProjectionValue, type RootType, type SearchSchema, type SearchType } from './schema.js';
|
|
4
4
|
/**
|
|
5
5
|
* A projected node: the fields of one {@link SearchType}, flat. A root always
|
|
6
6
|
* carries the `id` that keys it – it is a {@link SearchDocument} – but an inline
|
|
@@ -13,6 +13,21 @@ export type ProjectedNode = Record<string, unknown>;
|
|
|
13
13
|
export type SearchDocument = {
|
|
14
14
|
id: string;
|
|
15
15
|
} & ProjectedNode;
|
|
16
|
+
/**
|
|
17
|
+
* What the projection knows that the graph does not say: the values a run
|
|
18
|
+
* carries about the *indexing* rather than about the data. They reach a
|
|
19
|
+
* declaration two ways – a field declaring {@link KeywordField.from `from`} is
|
|
20
|
+
* populated from one, and every `derive` receives the whole context as its
|
|
21
|
+
* second argument, so a derive can relate a projected value to it.
|
|
22
|
+
*
|
|
23
|
+
* Empty when the caller supplies nothing: projecting a graph in isolation (a
|
|
24
|
+
* test, a one-off) is legitimate, and a field over an absent value is simply
|
|
25
|
+
* left unpopulated, exactly as a `path` that matched nothing is.
|
|
26
|
+
*/
|
|
27
|
+
export interface ProjectionContext extends Partial<Readonly<Record<ProjectionValue, string>>> {
|
|
28
|
+
/** The IRI of the dataset being indexed. */
|
|
29
|
+
readonly dataset?: string;
|
|
30
|
+
}
|
|
16
31
|
/**
|
|
17
32
|
* Project one framed JSON-LD node into a flat search document: apply each field
|
|
18
33
|
* of the type in declaration order. A field with a `derive` function computes
|
|
@@ -27,7 +42,7 @@ export type SearchDocument = {
|
|
|
27
42
|
* fans out to come from {@link physicalFields}, the single source shared with
|
|
28
43
|
* the engine collection definition and the query compiler.
|
|
29
44
|
*/
|
|
30
|
-
export declare function projectDocument(node: FramedNode, searchType: SearchType, schema?: SearchSchema): SearchDocument;
|
|
45
|
+
export declare function projectDocument(node: FramedNode, searchType: SearchType, schema?: SearchSchema, context?: ProjectionContext): SearchDocument;
|
|
31
46
|
/**
|
|
32
47
|
* Project a single type over a known set of `roots` – the per-type, roots-given
|
|
33
48
|
* projection. The roots are supplied by the caller (the pipeline selector)
|
|
@@ -41,5 +56,5 @@ export declare function projectDocument(node: FramedNode, searchType: SearchType
|
|
|
41
56
|
* Consumes `quads` once, so it accepts any `Iterable` – a batch’s materialized
|
|
42
57
|
* array or a chained generator merging several readers.
|
|
43
58
|
*/
|
|
44
|
-
export declare function projectRoots(quads: Iterable<Quad>, roots: readonly string[], schema: SearchSchema, searchType: RootType): AsyncIterable<SearchDocument>;
|
|
59
|
+
export declare function projectRoots(quads: Iterable<Quad>, roots: readonly string[], schema: SearchSchema, searchType: RootType, context?: ProjectionContext): AsyncIterable<SearchDocument>;
|
|
45
60
|
//# sourceMappingURL=project.d.ts.map
|
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,EAGL,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,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,EAWL,KAAK,eAAe,EAEpB,KAAK,QAAQ,EAEb,KAAK,YAAY,EACjB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAkB,SAAQ,OAAO,CAChD,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAC1C;IACC,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,UAAU,EACtB,MAAM,CAAC,EAAE,YAAY,EACrB,OAAO,GAAE,iBAAyC,GACjD,cAAc,CAgBhB;AAyED;;;;;;;;;;;;GAYG;AACH,wBAAuB,YAAY,CACjC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,QAAQ,EACpB,OAAO,GAAE,iBAAyC,GACjD,aAAa,CAAC,cAAc,CAAC,CAW/B"}
|
package/dist/project.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { fold } from '@lde/text-normalization';
|
|
2
2
|
import { buildSubjectIndex, frameSubjects, } from './frame-by-type.js';
|
|
3
3
|
import { assertTypeInSchema, displayFieldName, inlineFramingDepth, irAlias, isInternalField, isInlineReference, isoToUnixSeconds, physicalFields, referenceTypeNamed, } from './schema.js';
|
|
4
|
+
/** The context a projection runs with when its caller supplied none. */
|
|
5
|
+
const NO_PROJECTION_CONTEXT = {};
|
|
4
6
|
/**
|
|
5
7
|
* Project one framed JSON-LD node into a flat search document: apply each field
|
|
6
8
|
* of the type in declaration order. A field with a `derive` function computes
|
|
@@ -15,13 +17,13 @@ import { assertTypeInSchema, displayFieldName, inlineFramingDepth, irAlias, isIn
|
|
|
15
17
|
* fans out to come from {@link physicalFields}, the single source shared with
|
|
16
18
|
* the engine collection definition and the query compiler.
|
|
17
19
|
*/
|
|
18
|
-
export function projectDocument(node, searchType, schema) {
|
|
20
|
+
export function projectDocument(node, searchType, schema, context = NO_PROJECTION_CONTEXT) {
|
|
19
21
|
if (documentKey(node) === undefined) {
|
|
20
22
|
throw new Error(`Cannot project a “${searchType.name}” node without an @id (a blank node label is not one): every search document needs a stable key, and an empty one would collide with other keyless nodes.`);
|
|
21
23
|
}
|
|
22
24
|
// The guard above is what makes this a SearchDocument: projectFields sets `id`
|
|
23
25
|
// for exactly the nodes documentKey resolves.
|
|
24
|
-
const document = projectFields(node, searchType, schema);
|
|
26
|
+
const document = projectFields(node, searchType, schema, context);
|
|
25
27
|
pruneInternalFields(document, searchType, schema);
|
|
26
28
|
return document;
|
|
27
29
|
}
|
|
@@ -65,11 +67,11 @@ function pruneInternalFields(document, searchType, schema) {
|
|
|
65
67
|
* Pruning is deferred to a single recursive pass ({@link pruneInternalFields})
|
|
66
68
|
* once the whole nested structure is projected, so a derive at any depth can
|
|
67
69
|
* still read an internal field before it is removed. */
|
|
68
|
-
function projectFields(node, searchType, schema) {
|
|
70
|
+
function projectFields(node, searchType, schema, context) {
|
|
69
71
|
const id = documentKey(node);
|
|
70
72
|
const document = id === undefined ? {} : { id };
|
|
71
73
|
for (const field of searchType.fields) {
|
|
72
|
-
applyField(document, node, field, searchType, schema);
|
|
74
|
+
applyField(document, node, field, searchType, schema, context);
|
|
73
75
|
}
|
|
74
76
|
return document;
|
|
75
77
|
}
|
|
@@ -97,7 +99,7 @@ function documentKey(node) {
|
|
|
97
99
|
* Consumes `quads` once, so it accepts any `Iterable` – a batch’s materialized
|
|
98
100
|
* array or a chained generator merging several readers.
|
|
99
101
|
*/
|
|
100
|
-
export async function* projectRoots(quads, roots, schema, searchType) {
|
|
102
|
+
export async function* projectRoots(quads, roots, schema, searchType, context = NO_PROJECTION_CONTEXT) {
|
|
101
103
|
assertTypeInSchema(schema, searchType);
|
|
102
104
|
const index = buildSubjectIndex(quads);
|
|
103
105
|
// Distinct roots only. A selector may return an IRI more than once – a
|
|
@@ -106,12 +108,18 @@ export async function* projectRoots(quads, roots, schema, searchType) {
|
|
|
106
108
|
// duplicate document under the same `id`.
|
|
107
109
|
const depth = inlineFramingDepth(schema, searchType);
|
|
108
110
|
for await (const node of frameSubjects(index, [...new Set(roots)], depth)) {
|
|
109
|
-
yield projectDocument(node, searchType, schema);
|
|
111
|
+
yield projectDocument(node, searchType, schema, context);
|
|
110
112
|
}
|
|
111
113
|
}
|
|
112
|
-
function applyField(document, node, field, searchType, schema) {
|
|
114
|
+
function applyField(document, node, field, searchType, schema, context) {
|
|
115
|
+
// The three value sources, mutually exclusive by declaration
|
|
116
|
+
// (`validateSearchType`): a projection value, a computed value, a graph path.
|
|
117
|
+
if ((field.kind === 'keyword' || field.kind === 'reference') &&
|
|
118
|
+
field.from !== undefined) {
|
|
119
|
+
return applyProjectionValue(document, field, field.from, context);
|
|
120
|
+
}
|
|
113
121
|
if (field.derive !== undefined) {
|
|
114
|
-
const value = field.derive(document);
|
|
122
|
+
const value = field.derive(document, context);
|
|
115
123
|
if (value !== undefined) {
|
|
116
124
|
document[field.name] = value;
|
|
117
125
|
}
|
|
@@ -134,7 +142,7 @@ function applyField(document, node, field, searchType, schema) {
|
|
|
134
142
|
// project nothing rather than fall through and emit the referent IRIs under
|
|
135
143
|
// the field name (the wrong shape).
|
|
136
144
|
if (schema !== undefined) {
|
|
137
|
-
applyInlineReference(document, node, alias, field, schema);
|
|
145
|
+
applyInlineReference(document, node, alias, field, schema, context);
|
|
138
146
|
}
|
|
139
147
|
return;
|
|
140
148
|
}
|
|
@@ -241,6 +249,25 @@ function applyFacet(document, raw, field) {
|
|
|
241
249
|
setString(document, searchField, folded[0]);
|
|
242
250
|
}
|
|
243
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Project a field declared over a {@link ProjectionContext} value: read the
|
|
254
|
+
* value the run carries and write it exactly as a graph-read one, through
|
|
255
|
+
* {@link applyFacet} – so `array`, `transform` and the folded `searchable`
|
|
256
|
+
* companion mean the same thing for a declaration over the dataset as for one
|
|
257
|
+
* over a path, and the collection definition needs no special case.
|
|
258
|
+
*
|
|
259
|
+
* A value the context does not carry writes nothing: a projection run outside a
|
|
260
|
+
* pipeline (a test, a one-off) leaves the field absent rather than inventing a
|
|
261
|
+
* placeholder, the same as a `path` that matched nothing.
|
|
262
|
+
*/
|
|
263
|
+
function applyProjectionValue(document, field, from, context) {
|
|
264
|
+
// `ProjectionContext` is keyed by `ProjectionValue`, so the declaration reads
|
|
265
|
+
// the context directly: a projection value that has no context key, or a
|
|
266
|
+
// context key no declaration can name, is a compile error rather than a
|
|
267
|
+
// silently unpopulated field.
|
|
268
|
+
const value = context[from];
|
|
269
|
+
applyFacet(document, value === undefined ? [] : [value], field);
|
|
270
|
+
}
|
|
244
271
|
/**
|
|
245
272
|
* Project an inline reference: the referent node(s) embedded under the field’s
|
|
246
273
|
* {@link irAlias IR Alias} are each projected through the reference’s
|
|
@@ -258,7 +285,7 @@ function applyFacet(document, raw, field) {
|
|
|
258
285
|
* the schema’s declared depth. The referent type is guaranteed declared by
|
|
259
286
|
* {@link searchSchema}.
|
|
260
287
|
*/
|
|
261
|
-
function applyInlineReference(document, node, alias, field, schema) {
|
|
288
|
+
function applyInlineReference(document, node, alias, field, schema, context) {
|
|
262
289
|
// Resolves for a schema that declares the referent (always so for the schema a
|
|
263
290
|
// type is projected through); a type framed against a foreign schema that
|
|
264
291
|
// omits it simply contributes no nesting.
|
|
@@ -268,7 +295,7 @@ function applyInlineReference(document, node, alias, field, schema) {
|
|
|
268
295
|
}
|
|
269
296
|
const referents = valuesOf(node, alias)
|
|
270
297
|
.filter(isObject)
|
|
271
|
-
.map((referent) => projectFields(referent, referenceType, schema))
|
|
298
|
+
.map((referent) => projectFields(referent, referenceType, schema, context))
|
|
272
299
|
// Fields, not identity, are what makes something a referent: a literal
|
|
273
300
|
// value object under the alias (dirty source data), or a node this
|
|
274
301
|
// reference type reads nothing from, projects nothing and is no referent.
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ProjectedNode } from './project.js';
|
|
1
|
+
import type { ProjectedNode, ProjectionContext } from './project.js';
|
|
2
2
|
/**
|
|
3
3
|
* The engine-neutral kind of a queryable field. It drives every downstream
|
|
4
4
|
* behavior: which physical fields the projection emits, the engine
|
|
@@ -33,9 +33,13 @@ export declare function filterOperatorFor(kind: FieldKind): FilterOperator | und
|
|
|
33
33
|
* field with a {@link SearchFieldBase.derive `derive`} function instead of a
|
|
34
34
|
* `path` is a **derived field** – computed from the document projected so far
|
|
35
35
|
* rather than read from the graph – yet it still carries full query/schema/output
|
|
36
|
-
* behavior (e.g. `status`, the compatibility booleans). A
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* behavior (e.g. `status`, the compatibility booleans). A `keyword`/`reference`
|
|
37
|
+
* field may instead declare {@link KeywordField.from `from`}, populating it from
|
|
38
|
+
* a {@link ProjectionValue} – something the run knows and the graph does not.
|
|
39
|
+
* `path`, `derive` and `from` are the three mutually exclusive value sources. A
|
|
40
|
+
* field declaring **no** role at all is an {@link isInternalField **internal
|
|
41
|
+
* field**}: projected so a later derive can read it, then pruned before the
|
|
42
|
+
* writer.
|
|
39
43
|
*
|
|
40
44
|
* The physical field names a declaration fans out to (per-locale search/sort
|
|
41
45
|
* keys) follow one convention, owned by
|
|
@@ -89,9 +93,30 @@ export interface SearchFieldBase {
|
|
|
89
93
|
* is the search document for a root type, and the referent’s own
|
|
90
94
|
* projected fields for a Reference Type – which carry an `id` only when the
|
|
91
95
|
* referent is a named node, never for a blank-node one.
|
|
96
|
+
*
|
|
97
|
+
* The second argument is the {@link ProjectionContext} – the run’s
|
|
98
|
+
* projection-time values, which are about the *indexing*, not the graph (the
|
|
99
|
+
* dataset being indexed). It is what lets a derive relate a projected value
|
|
100
|
+
* to its provenance: dropping a polymorphic `isPartOf` value that merely
|
|
101
|
+
* points back at the containing dataset, say.
|
|
92
102
|
*/
|
|
93
|
-
readonly derive?: (document: ProjectedNode) => unknown;
|
|
103
|
+
readonly derive?: (document: ProjectedNode, context: ProjectionContext) => unknown;
|
|
94
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* A projection-time value a field can be declared **over** – something the run
|
|
107
|
+
* knows that the graph does not say. `dataset` is the IRI of the dataset being
|
|
108
|
+
* indexed: every indexed document comes from exactly one, and for the many
|
|
109
|
+
* entity types that carry no containing-collection property (`Person`,
|
|
110
|
+
* `Organization`, `Place`, …) it is the only available answer to *which dataset
|
|
111
|
+
* does this come from*.
|
|
112
|
+
*
|
|
113
|
+
* A field declaring one is populated by the projection, like a `path`-bearing
|
|
114
|
+
* or `derive`d field, and carries the full range of behaviour: `output`,
|
|
115
|
+
* `filterable`, `facetable`, and – for a `reference` – a
|
|
116
|
+
* {@link ReferenceField.labelSource `labelSource`} that resolves the IRI to a
|
|
117
|
+
* readable label at query time.
|
|
118
|
+
*/
|
|
119
|
+
export type ProjectionValue = 'dataset';
|
|
95
120
|
/** Full-text inclusion with a `query_by` weight (folded; per-locale for
|
|
96
121
|
* localized text). Presence is what makes a field searchable. */
|
|
97
122
|
export interface Searchable {
|
|
@@ -127,6 +152,9 @@ export interface KeywordField extends SearchFieldBase, Searchable {
|
|
|
127
152
|
/** Projection-time value transform (e.g. strip a media-type prefix). */
|
|
128
153
|
readonly transform?: (value: string) => string;
|
|
129
154
|
readonly facetRanges?: never;
|
|
155
|
+
/** Populate from a {@link ProjectionValue} instead of the graph. Mutually
|
|
156
|
+
* exclusive with `path` and `derive`. */
|
|
157
|
+
readonly from?: ProjectionValue;
|
|
130
158
|
}
|
|
131
159
|
/** An IRI-valued reference to another entity, label-resolved at the surface. */
|
|
132
160
|
export interface ReferenceField extends SearchFieldBase, Searchable {
|
|
@@ -134,6 +162,11 @@ export interface ReferenceField extends SearchFieldBase, Searchable {
|
|
|
134
162
|
/** Projection-time value transform. */
|
|
135
163
|
readonly transform?: (value: string) => string;
|
|
136
164
|
readonly facetRanges?: never;
|
|
165
|
+
/** Populate from a {@link ProjectionValue} instead of the graph – a
|
|
166
|
+
* `dataset` reference is how a type declares the dataset it was indexed
|
|
167
|
+
* from, resolvable to a label like any other reference. Mutually exclusive
|
|
168
|
+
* with `path` and `derive`. */
|
|
169
|
+
readonly from?: ProjectionValue;
|
|
137
170
|
/**
|
|
138
171
|
* The `name` of the {@link SearchType} whose collection resolves this
|
|
139
172
|
* reference’s labels – its ‘label source’. The named type must declare an
|
|
@@ -375,7 +408,7 @@ export declare function labelFieldOf(searchType: SearchType): TextField | undefi
|
|
|
375
408
|
*/
|
|
376
409
|
export interface SearchTypeIssue {
|
|
377
410
|
readonly field: string;
|
|
378
|
-
readonly reason: 'duplicate-field-name' | 'invalid-field-name' | 'unknown-kind' | 'invalid-locale' | 'missing-ref' | 'ref-not-allowed' | 'text-requires-locales' | 'locales-not-allowed' | 'facet-ranges-not-allowed' | 'searchable-not-allowed' | 'transform-not-allowed' | 'derive-with-path' | 'text-not-filterable' | 'text-not-facetable' | 'reserved-field-name';
|
|
411
|
+
readonly reason: 'duplicate-field-name' | 'invalid-field-name' | 'unknown-kind' | 'invalid-locale' | 'missing-ref' | 'ref-not-allowed' | 'text-requires-locales' | 'locales-not-allowed' | 'facet-ranges-not-allowed' | 'searchable-not-allowed' | 'transform-not-allowed' | 'derive-with-path' | 'from-not-allowed' | 'from-with-path' | 'from-with-derive' | 'from-with-inline-ref' | 'unknown-projection-value' | 'duplicate-projection-value' | 'text-not-filterable' | 'text-not-facetable' | 'reserved-field-name';
|
|
379
412
|
}
|
|
380
413
|
/**
|
|
381
414
|
* The reserved logical name of a document’s IRI – the one field every indexed
|
|
@@ -414,7 +447,13 @@ export declare const ID_FIELD = "id";
|
|
|
414
447
|
* - `transform` only on `keyword`/`reference` (the only kinds whose
|
|
415
448
|
* projection applies it);
|
|
416
449
|
* - `derive` and `path` are mutually exclusive (a field is projected or
|
|
417
|
-
* computed, never both)
|
|
450
|
+
* computed, never both);
|
|
451
|
+
* - `from` names a known {@link ProjectionValue}, sits on a `keyword`/`reference`
|
|
452
|
+
* field, excludes `path` and `derive` (the three are the value sources, and a
|
|
453
|
+
* field has exactly one), is not an `inline` reference (a projection value is
|
|
454
|
+
* a bare IRI, with no referent to carry fields from), and no two fields
|
|
455
|
+
* declare the same projection value – which would leave a consumer reading
|
|
456
|
+
* the dataset off a declaration no rule picks between.
|
|
418
457
|
*
|
|
419
458
|
* Pure and total: returns every issue rather than throwing;
|
|
420
459
|
* {@link assertValidSearchType} is the throwing entry point.
|
|
@@ -514,6 +553,18 @@ export declare function outputFields(searchType: SearchType): readonly SearchFie
|
|
|
514
553
|
export declare function isInternalField(field: SearchField): boolean;
|
|
515
554
|
/** Fields of kind `reference` (IRI-valued, label-resolved), in declaration order. */
|
|
516
555
|
export declare function referenceFields(searchType: SearchType): readonly ReferenceField[];
|
|
556
|
+
/**
|
|
557
|
+
* The field a type declares over the dataset being indexed
|
|
558
|
+
* ({@link ProjectionValue `from: 'dataset'`}), if it declares one.
|
|
559
|
+
* {@link validateSearchType} rejects a second field over the same projection
|
|
560
|
+
* value, so for any type that reached a {@link SearchSchema} this is the only
|
|
561
|
+
* such field rather than the first of several.
|
|
562
|
+
*
|
|
563
|
+
* The single lookup every consumer of the declaration shares – the projection
|
|
564
|
+
* that populates it, and the engine writer that keeps its provenance
|
|
565
|
+
* bookkeeping on it rather than on a private field of its own.
|
|
566
|
+
*/
|
|
567
|
+
export declare function datasetField(searchType: SearchType): (KeywordField | ReferenceField) | undefined;
|
|
517
568
|
/** Look up a field by its logical name. */
|
|
518
569
|
export declare function fieldNamed(searchType: SearchType, name: string): SearchField | undefined;
|
|
519
570
|
/**
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.js
CHANGED
|
@@ -316,6 +316,15 @@ export const ID_FIELD = 'id';
|
|
|
316
316
|
const SEARCHABLE_KINDS = ['text', 'keyword', 'reference'];
|
|
317
317
|
/** Kinds whose projection applies the {@link KeywordField.transform}. */
|
|
318
318
|
const TRANSFORMABLE_KINDS = ['keyword', 'reference'];
|
|
319
|
+
/**
|
|
320
|
+
* Kinds a {@link ProjectionValue} can populate: every projection value is an
|
|
321
|
+
* IRI or a token, so only the two string-shaped kinds can hold one. `reference`
|
|
322
|
+
* is what carries a {@link ReferenceField.labelSource}, and so the kind a
|
|
323
|
+
* facet over the dataset wants.
|
|
324
|
+
*/
|
|
325
|
+
const FROM_KINDS = ['keyword', 'reference'];
|
|
326
|
+
/** The {@link ProjectionValue}s a declaration may name. */
|
|
327
|
+
const PROJECTION_VALUES = ['dataset'];
|
|
319
328
|
/**
|
|
320
329
|
* A safe logical field name: a GraphQL-style identifier. The name is
|
|
321
330
|
* interpolated raw into physical field names AND, for a display text field,
|
|
@@ -356,7 +365,13 @@ const LOCALE_PATTERN = /^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$/;
|
|
|
356
365
|
* - `transform` only on `keyword`/`reference` (the only kinds whose
|
|
357
366
|
* projection applies it);
|
|
358
367
|
* - `derive` and `path` are mutually exclusive (a field is projected or
|
|
359
|
-
* computed, never both)
|
|
368
|
+
* computed, never both);
|
|
369
|
+
* - `from` names a known {@link ProjectionValue}, sits on a `keyword`/`reference`
|
|
370
|
+
* field, excludes `path` and `derive` (the three are the value sources, and a
|
|
371
|
+
* field has exactly one), is not an `inline` reference (a projection value is
|
|
372
|
+
* a bare IRI, with no referent to carry fields from), and no two fields
|
|
373
|
+
* declare the same projection value – which would leave a consumer reading
|
|
374
|
+
* the dataset off a declaration no rule picks between.
|
|
360
375
|
*
|
|
361
376
|
* Pure and total: returns every issue rather than throwing;
|
|
362
377
|
* {@link assertValidSearchType} is the throwing entry point.
|
|
@@ -364,6 +379,7 @@ const LOCALE_PATTERN = /^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$/;
|
|
|
364
379
|
export function validateSearchType(searchType) {
|
|
365
380
|
const issues = [];
|
|
366
381
|
const seen = new Set();
|
|
382
|
+
const seenProjectionValues = new Set();
|
|
367
383
|
for (const declared of searchType.fields) {
|
|
368
384
|
// Validation guards declarations built OUTSIDE TypeScript (a SHACL
|
|
369
385
|
// generator, plain JS), so it inspects the uniform flat shape rather
|
|
@@ -439,6 +455,35 @@ export function validateSearchType(searchType) {
|
|
|
439
455
|
if (field.derive !== undefined && field.path !== undefined) {
|
|
440
456
|
issue('derive-with-path');
|
|
441
457
|
}
|
|
458
|
+
if (field.from !== undefined) {
|
|
459
|
+
if (!PROJECTION_VALUES.includes(field.from)) {
|
|
460
|
+
issue('unknown-projection-value');
|
|
461
|
+
}
|
|
462
|
+
else if (seenProjectionValues.has(field.from)) {
|
|
463
|
+
// Two fields over one projection value leaves every consumer that
|
|
464
|
+
// resolves the value back to a declaration – the writer’s provenance
|
|
465
|
+
// field, above all – with no rule to pick between them.
|
|
466
|
+
issue('duplicate-projection-value');
|
|
467
|
+
}
|
|
468
|
+
seenProjectionValues.add(field.from);
|
|
469
|
+
if (!FROM_KINDS.includes(field.kind)) {
|
|
470
|
+
issue('from-not-allowed');
|
|
471
|
+
}
|
|
472
|
+
if (field.path !== undefined) {
|
|
473
|
+
issue('from-with-path');
|
|
474
|
+
}
|
|
475
|
+
if (field.derive !== undefined) {
|
|
476
|
+
issue('from-with-derive');
|
|
477
|
+
}
|
|
478
|
+
// An inline reference carries a referent’s projected fields, and the
|
|
479
|
+
// collection definition declares it as a nested object. A projection
|
|
480
|
+
// value has no referent – it is a bare IRI – so the two together would
|
|
481
|
+
// declare an object field the projection fills with a string, and every
|
|
482
|
+
// document import would fail.
|
|
483
|
+
if (field.ref?.strategy === 'inline') {
|
|
484
|
+
issue('from-with-inline-ref');
|
|
485
|
+
}
|
|
486
|
+
}
|
|
442
487
|
}
|
|
443
488
|
return issues;
|
|
444
489
|
}
|
|
@@ -565,6 +610,21 @@ export function isInternalField(field) {
|
|
|
565
610
|
export function referenceFields(searchType) {
|
|
566
611
|
return searchType.fields.filter((field) => field.kind === 'reference');
|
|
567
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* The field a type declares over the dataset being indexed
|
|
615
|
+
* ({@link ProjectionValue `from: 'dataset'`}), if it declares one.
|
|
616
|
+
* {@link validateSearchType} rejects a second field over the same projection
|
|
617
|
+
* value, so for any type that reached a {@link SearchSchema} this is the only
|
|
618
|
+
* such field rather than the first of several.
|
|
619
|
+
*
|
|
620
|
+
* The single lookup every consumer of the declaration shares – the projection
|
|
621
|
+
* that populates it, and the engine writer that keeps its provenance
|
|
622
|
+
* bookkeeping on it rather than on a private field of its own.
|
|
623
|
+
*/
|
|
624
|
+
export function datasetField(searchType) {
|
|
625
|
+
return searchType.fields.find((field) => (field.kind === 'keyword' || field.kind === 'reference') &&
|
|
626
|
+
field.from === 'dataset');
|
|
627
|
+
}
|
|
568
628
|
/** Look up a field by its logical name. */
|
|
569
629
|
export function fieldNamed(searchType, name) {
|
|
570
630
|
return searchType.fields.find((field) => field.name === name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.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",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@tpluscode/rdf-ns-builders": "^5.0.0",
|
|
54
54
|
"@types/jsonld": "^1.5.15",
|
|
55
|
-
"n3": "^2.1.
|
|
55
|
+
"n3": "^2.1.2"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"vitest": "^4.0.0"
|