@lde/search 0.22.1 → 0.23.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/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 +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +82 -5
- package/dist/query.d.ts +23 -5
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +26 -3
- package/dist/schema.d.ts +107 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +165 -14
- package/package.json +1 -1
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, datasetField, ID_FIELD, AND_KEY, OR_KEY, labelFieldOf, labelFieldNameOf, labelSourceNameOf, DEFAULT_LABEL_FIELD, isRangeFacet, isAbsoluteIri, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
9
|
+
export { assertTypeInSchema, physicalFields, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, referenceFields, referenceTypeNamed, rootTypeNamed, inlineFramingDepth, fieldNamed, datasetField, ID_FIELD, AND_KEY, OR_KEY, labelFieldOf, labelFieldNameOf, labelSourceNameOf, documentKeyOf, DEFAULT_LABEL_FIELD, isRangeFacet, isAbsoluteIri, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
10
10
|
export type { PhysicalFields } from './schema.js';
|
|
11
11
|
export { physicalNameTokens } from './physical-name.js';
|
|
12
12
|
export { joinGraph, MAX_JOIN_DEPTH } from './join-graph.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,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,MAAM,EACN,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,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,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,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,aAAa,EACb,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,MAAM,EACN,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,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,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,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,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, datasetField, ID_FIELD, AND_KEY, OR_KEY, labelFieldOf, labelFieldNameOf, labelSourceNameOf, DEFAULT_LABEL_FIELD, isRangeFacet, isAbsoluteIri, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
9
|
+
export { assertTypeInSchema, physicalFields, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, referenceFields, referenceTypeNamed, rootTypeNamed, inlineFramingDepth, fieldNamed, datasetField, ID_FIELD, AND_KEY, OR_KEY, labelFieldOf, labelFieldNameOf, labelSourceNameOf, documentKeyOf, DEFAULT_LABEL_FIELD, isRangeFacet, isAbsoluteIri, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
10
10
|
export { physicalNameTokens } from './physical-name.js';
|
|
11
11
|
export { joinGraph, MAX_JOIN_DEPTH } from './join-graph.js';
|
|
12
12
|
export { filterOperatorFor, filterOperator, filterOn, 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
2
|
export type { ProjectedNode, ProjectionContext, SearchDocument, } from './project.js';
|
|
3
3
|
export { defineSearchType, searchSchema, validateSearchType, assertValidSearchType, ID_FIELD, AND_KEY, OR_KEY, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
4
|
-
export type { FieldKind, SearchField, SearchFieldBase, TextField, KeywordField, ReferenceField, ReferenceStrategy, NumericField, BooleanField, SearchType, SearchTypeBase, RootType, ReferenceType, RootTypeOf, SearchTypeIssue, SearchSchema, FacetRange, ProjectionValue, } from './schema.js';
|
|
4
|
+
export type { FieldKind, SearchField, SearchFieldBase, TextField, KeywordField, ReferenceField, ReferenceStrategy, NumericField, BooleanField, SearchType, SearchTypeBase, RootType, ReferenceType, RootTypeOf, SearchTypeIssue, KeyField, SearchSchema, FacetRange, ProjectionValue, } from './schema.js';
|
|
5
5
|
export { joinGraph, MAX_JOIN_DEPTH } from './join-graph.js';
|
|
6
6
|
export type { JoinGraph } from './join-graph.js';
|
|
7
7
|
export { filterOn } 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,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,EACR,OAAO,EACP,MAAM,EAMN,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,SAAS,EACT,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,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;AAKrB,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAOjD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,MAAM,EACN,SAAS,EACT,aAAa,EACb,IAAI,GACL,MAAM,YAAY,CAAC;AAIpB,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"}
|
|
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,EACR,OAAO,EACP,MAAM,EAMN,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,SAAS,EACT,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,cAAc,EACd,QAAQ,EACR,aAAa,EACb,UAAU,EACV,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,eAAe,GAChB,MAAM,aAAa,CAAC;AAKrB,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAOjD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,MAAM,EACN,SAAS,EACT,aAAa,EACb,IAAI,GACL,MAAM,YAAY,CAAC;AAIpB,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.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,EAgBL,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;AAmHD;;;;;;;;;;;;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,CAc/B"}
|
package/dist/project.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fold } from '@lde/text-normalization';
|
|
2
2
|
import { buildSubjectIndex, frameSubjects, } from './frame-by-type.js';
|
|
3
|
-
import { assertTypeInSchema, displayFieldName, inlineFramingDepth, irAlias, isAbsoluteIri, isInternalField, isInlineReference, isoToUnixSeconds, physicalFields, referenceTypeNamed, } from './schema.js';
|
|
3
|
+
import { assertTypeInSchema, displayFieldName, documentKeyOf, fieldNamed, inlineFramingDepth, irAlias, isAbsoluteIri, isInternalField, isInlineReference, isoToUnixSeconds, labelSourceNameOf, physicalFields, referenceTypeNamed, rootTypeNamed, } from './schema.js';
|
|
4
4
|
/** The context a projection runs with when its caller supplied none. */
|
|
5
5
|
const NO_PROJECTION_CONTEXT = {};
|
|
6
6
|
/**
|
|
@@ -68,7 +68,7 @@ function pruneInternalFields(document, searchType, schema) {
|
|
|
68
68
|
* once the whole nested structure is projected, so a derive at any depth can
|
|
69
69
|
* still read an internal field before it is removed. */
|
|
70
70
|
function projectFields(node, searchType, schema, context) {
|
|
71
|
-
const id =
|
|
71
|
+
const id = documentIdOf(node, searchType);
|
|
72
72
|
const document = id === undefined ? {} : { id };
|
|
73
73
|
for (const field of searchType.fields) {
|
|
74
74
|
applyField(document, node, field, searchType, schema, context);
|
|
@@ -88,6 +88,33 @@ function documentKey(node) {
|
|
|
88
88
|
const id = node['@id'];
|
|
89
89
|
return typeof id === 'string' && isAbsoluteIri(id) ? id : undefined;
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* The `id` a node is projected under: its own IRI, unless its type declares a
|
|
93
|
+
* {@link RootType.key} – then the key its key field carries
|
|
94
|
+
* ({@link documentKeyOf}). The candidates are read off the frame like any other
|
|
95
|
+
* field value, under the key field’s own {@link irAlias IR Alias}, because the
|
|
96
|
+
* key field *is* an ordinary declared field: the extraction already emits it,
|
|
97
|
+
* and a reader transform that repairs reference values has already run on it.
|
|
98
|
+
*
|
|
99
|
+
* The key is assigned before any `derive` runs, so a derive sees the key and
|
|
100
|
+
* never the node IRI; a deployment that wants the node IRI declares a plain
|
|
101
|
+
* `idOnly` reference over the same path.
|
|
102
|
+
*/
|
|
103
|
+
function documentIdOf(node, searchType) {
|
|
104
|
+
const nodeIri = documentKey(node);
|
|
105
|
+
if (nodeIri === undefined || searchType.key === undefined) {
|
|
106
|
+
return nodeIri;
|
|
107
|
+
}
|
|
108
|
+
return documentKeyOf(searchType, nodeIri, keyCandidatesOf(node, searchType, searchType.key.field));
|
|
109
|
+
}
|
|
110
|
+
/** The raw values a node carries under a type’s key field – untransformed, as
|
|
111
|
+
* {@link documentKeyOf} expects them. Empty for a node the key field matched
|
|
112
|
+
* nothing on. The field itself is guaranteed declared by `searchSchema`, which
|
|
113
|
+
* is what validates a `key` at all. */
|
|
114
|
+
function keyCandidatesOf(node, searchType, name) {
|
|
115
|
+
const keyField = fieldNamed(searchType, name);
|
|
116
|
+
return irisOf(node, irAlias(searchType, keyField));
|
|
117
|
+
}
|
|
91
118
|
/**
|
|
92
119
|
* Project a single type over a known set of `roots` – the per-type, roots-given
|
|
93
120
|
* projection. The roots are supplied by the caller (the pipeline selector)
|
|
@@ -106,8 +133,11 @@ export async function* projectRoots(quads, roots, schema, searchType, context =
|
|
|
106
133
|
const index = buildSubjectIndex(quads);
|
|
107
134
|
// Distinct roots only. A selector may return an IRI more than once – a
|
|
108
135
|
// non-`DISTINCT` `SELECT` over a one-to-many join yields the same subject per
|
|
109
|
-
// matched row – and a
|
|
110
|
-
//
|
|
136
|
+
// matched row – and framing a root twice would do the same work twice for one
|
|
137
|
+
// node. Distinct roots may still legitimately share an `id`, where the type
|
|
138
|
+
// declares a `key` two of them carry: that is what a document key means, and
|
|
139
|
+
// it is the writer’s upsert – not the projection – that folds them into one
|
|
140
|
+
// document.
|
|
111
141
|
const depth = inlineFramingDepth(schema, searchType);
|
|
112
142
|
for await (const node of frameSubjects(index, [...new Set(roots)], depth)) {
|
|
113
143
|
yield projectDocument(node, searchType, schema, context);
|
|
@@ -179,7 +209,7 @@ function applyField(document, node, field, searchType, schema, context) {
|
|
|
179
209
|
case 'keyword':
|
|
180
210
|
return applyFacet(document, literalsOf(node, alias), field);
|
|
181
211
|
case 'reference':
|
|
182
|
-
return applyFacet(document,
|
|
212
|
+
return applyFacet(document, referenceValues(node, alias, field, schema), field);
|
|
183
213
|
case 'integer':
|
|
184
214
|
return setNumber(document, field.name, toInteger(firstLiteralOf(node, alias)));
|
|
185
215
|
case 'number':
|
|
@@ -198,6 +228,53 @@ function applyField(document, node, field, searchType, schema, context) {
|
|
|
198
228
|
}
|
|
199
229
|
}
|
|
200
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* The values a non-inline reference stores: the referents’ IRIs, **or their
|
|
233
|
+
* document keys** when the type the reference names declares a
|
|
234
|
+
* {@link RootType.key}. A `lookup`’s `target` and an `idOnly`’s `labelSource`
|
|
235
|
+
* both mean *this field holds ids of documents in that collection* (ADR 20) –
|
|
236
|
+
* the fact a label lookup and a join rely on – so storing the node IRI where
|
|
237
|
+
* the target keys on something else would break an invariant the schema already
|
|
238
|
+
* has. Rewriting a reference is LDE keeping it, not a new rule.
|
|
239
|
+
*
|
|
240
|
+
* That is also why the boundary is *naming the target*: a reference that names
|
|
241
|
+
* none ({@link labelSourceNameOf} → `undefined`) never claimed to hold a
|
|
242
|
+
* collection’s ids, and a `derive`d one produces its own values rather than
|
|
243
|
+
* reading a referent, so neither is re-keyed.
|
|
244
|
+
*
|
|
245
|
+
* The referent’s candidates are in the frame because framing already embeds a
|
|
246
|
+
* referenced node’s own triples at one hop, and the extraction adds the key
|
|
247
|
+
* field to that hop. A projection run without a `schema` cannot resolve the
|
|
248
|
+
* target, so it leaves the values as they are – exactly as it leaves an inline
|
|
249
|
+
* reference unprojected.
|
|
250
|
+
*
|
|
251
|
+
* The referring field’s own {@link ReferenceField.transform} still runs after
|
|
252
|
+
* this, in {@link applyFacet}: a `transform` transforms what the field stores,
|
|
253
|
+
* and for a keyed target that is the key. A `transform` written to repair the
|
|
254
|
+
* referent’s node IRIs therefore sees a key instead, which is why the two are
|
|
255
|
+
* worth declaring together only deliberately.
|
|
256
|
+
*/
|
|
257
|
+
function referenceValues(node, alias, field, schema) {
|
|
258
|
+
const targetName = labelSourceNameOf(field);
|
|
259
|
+
if (schema === undefined || targetName === undefined) {
|
|
260
|
+
return irisOf(node, alias);
|
|
261
|
+
}
|
|
262
|
+
// Guaranteed declared: `searchSchema` validates that every named target
|
|
263
|
+
// resolves, and the projection only ever runs against a type of its schema.
|
|
264
|
+
const target = rootTypeNamed(schema, targetName);
|
|
265
|
+
if (target.key === undefined) {
|
|
266
|
+
return irisOf(node, alias);
|
|
267
|
+
}
|
|
268
|
+
const keyFieldName = target.key.field;
|
|
269
|
+
return valuesOf(node, alias)
|
|
270
|
+
.map((value) => {
|
|
271
|
+
const iri = iriString(value);
|
|
272
|
+
return iri === undefined
|
|
273
|
+
? undefined
|
|
274
|
+
: documentKeyOf(target, iri, isObject(value) ? keyCandidatesOf(value, target, keyFieldName) : []);
|
|
275
|
+
})
|
|
276
|
+
.filter((value) => value !== undefined);
|
|
277
|
+
}
|
|
201
278
|
/**
|
|
202
279
|
* Project a text field. **Display** (when `output`) preserves *every* language
|
|
203
280
|
* present – one label per language (accents preserved, untagged under `und`),
|
package/dist/query.d.ts
CHANGED
|
@@ -154,9 +154,10 @@ export declare function filterOn(criterion: Criterion): Filter;
|
|
|
154
154
|
export declare function isUnsatisfiable(query: SearchQuery): boolean;
|
|
155
155
|
/**
|
|
156
156
|
* One structural problem {@link validateQuery} found: the query references a
|
|
157
|
-
* field the search type does not declare,
|
|
158
|
-
*
|
|
159
|
-
*
|
|
157
|
+
* field the search type does not declare, uses it in a role it does not opt
|
|
158
|
+
* into, or constrains it with a value that cannot be read. Reported per
|
|
159
|
+
* **criterion**, so a clause carrying two criteria over unknown fields yields
|
|
160
|
+
* two issues, each naming its own field.
|
|
160
161
|
*
|
|
161
162
|
* Vacuous-but-valid clauses (an empty `in` list, a `range` with no bound, a
|
|
162
163
|
* clause with no criteria) are NOT issues – a compiler skips those as no-ops.
|
|
@@ -164,6 +165,9 @@ export declare function isUnsatisfiable(query: SearchQuery): boolean;
|
|
|
164
165
|
export interface QueryIssue {
|
|
165
166
|
readonly part: 'where' | 'facets' | 'orderBy' | 'resolve';
|
|
166
167
|
readonly field: string;
|
|
168
|
+
/** The offending literal, on an issue about a **value** rather than a field:
|
|
169
|
+
* the bound of an `unparseable-bound`. Absent on every other reason. */
|
|
170
|
+
readonly value?: string;
|
|
167
171
|
readonly reason: 'unknown-field' | 'not-filterable' | 'operator-mismatch' | 'not-facetable'
|
|
168
172
|
/** The criterion’s `on` path is longer than {@link MAX_JOIN_DEPTH}. */
|
|
169
173
|
| 'join-too-deep'
|
|
@@ -171,7 +175,18 @@ export interface QueryIssue {
|
|
|
171
175
|
* that is not `joinable`, or no join graph to resolve it against. */
|
|
172
176
|
| 'unknown-join'
|
|
173
177
|
/** A projected reference is not a `lookup`, so nothing resolves it. */
|
|
174
|
-
| 'not-resolvable'
|
|
178
|
+
| 'not-resolvable'
|
|
179
|
+
/**
|
|
180
|
+
* A `date` range bound the storage codec ({@link isoToUnixSeconds}) cannot
|
|
181
|
+
* read, so there is no stored value to compare against – reported per
|
|
182
|
+
* bound, each issue naming its own {@link QueryIssue.value}.
|
|
183
|
+
*
|
|
184
|
+
* Caught here rather than left to a compiler because the alternatives both
|
|
185
|
+
* lie: a dropped bound answers a *wider* question than the caller asked,
|
|
186
|
+
* and a rejected criterion answers a narrower one. Neither is the query,
|
|
187
|
+
* and only the caller can fix the bound.
|
|
188
|
+
*/
|
|
189
|
+
| 'unparseable-bound';
|
|
175
190
|
}
|
|
176
191
|
/**
|
|
177
192
|
* Structurally validate a query against its search type: **every criterion of
|
|
@@ -180,7 +195,10 @@ export interface QueryIssue {
|
|
|
180
195
|
* always-filterable {@link ID_FIELD}, which takes `in`; every requested facet is
|
|
181
196
|
* a declared, `facetable` field; every sort is `relevance` or a declared field.
|
|
182
197
|
* Because each criterion is matched against its own field’s kind, one clause may
|
|
183
|
-
* range over fields of different kinds.
|
|
198
|
+
* range over fields of different kinds. A `date` range is checked one step
|
|
199
|
+
* further, on its **values**: a bound the storage codec cannot read is an
|
|
200
|
+
* `unparseable-bound`, the one place a criterion is rejected for what it says
|
|
201
|
+
* rather than for what it names. Sorting deliberately
|
|
184
202
|
* checks declaration only, not the `sortable` flag: that flag means *publicly
|
|
185
203
|
* selectable*, and a deployment policy may sort on a private tie-break field.
|
|
186
204
|
*
|
package/dist/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|
package/dist/query.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fieldNamed, filterOperatorFor, ID_FIELD, } from './schema.js';
|
|
1
|
+
import { fieldNamed, filterOperatorFor, ID_FIELD, isoToUnixSeconds, } from './schema.js';
|
|
2
2
|
import { MAX_JOIN_DEPTH } from './join-graph.js';
|
|
3
3
|
// The kind→operator table lives with the field model in schema.ts (one source
|
|
4
4
|
// for the query IR, the surfaces, the adapters AND declaration validation);
|
|
@@ -64,7 +64,10 @@ function issueField(criterion) {
|
|
|
64
64
|
* always-filterable {@link ID_FIELD}, which takes `in`; every requested facet is
|
|
65
65
|
* a declared, `facetable` field; every sort is `relevance` or a declared field.
|
|
66
66
|
* Because each criterion is matched against its own field’s kind, one clause may
|
|
67
|
-
* range over fields of different kinds.
|
|
67
|
+
* range over fields of different kinds. A `date` range is checked one step
|
|
68
|
+
* further, on its **values**: a bound the storage codec cannot read is an
|
|
69
|
+
* `unparseable-bound`, the one place a criterion is rejected for what it says
|
|
70
|
+
* rather than for what it names. Sorting deliberately
|
|
68
71
|
* checks declaration only, not the `sortable` flag: that flag means *publicly
|
|
69
72
|
* selectable*, and a deployment policy may sort on a private tie-break field.
|
|
70
73
|
*
|
|
@@ -143,6 +146,23 @@ export function validateQuery(query, searchType, schema, joins) {
|
|
|
143
146
|
reason: 'operator-mismatch',
|
|
144
147
|
});
|
|
145
148
|
}
|
|
149
|
+
else if (declared.kind === 'date' && 'range' in criterion) {
|
|
150
|
+
// A `date` bound is ISO 8601 at the edge and Unix seconds in storage,
|
|
151
|
+
// so a bound the codec cannot read has nothing to compare against.
|
|
152
|
+
// Both bounds are checked, so a caller who wrote two bad ones is told
|
|
153
|
+
// about both instead of fixing one to be told about the other.
|
|
154
|
+
for (const bound of [criterion.range.min, criterion.range.max]) {
|
|
155
|
+
if (typeof bound === 'string' &&
|
|
156
|
+
isoToUnixSeconds(bound) === undefined) {
|
|
157
|
+
issues.push({
|
|
158
|
+
part: 'where',
|
|
159
|
+
field,
|
|
160
|
+
value: bound,
|
|
161
|
+
reason: 'unparseable-bound',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
146
166
|
}
|
|
147
167
|
}
|
|
148
168
|
for (const name of query.facets) {
|
|
@@ -213,7 +233,10 @@ export function assertValidQuery(query, searchType, schema, joins) {
|
|
|
213
233
|
const issues = validateQuery(query, searchType, schema, joins);
|
|
214
234
|
if (issues.length > 0) {
|
|
215
235
|
const detail = issues
|
|
216
|
-
.map((issue) =>
|
|
236
|
+
.map((issue) => {
|
|
237
|
+
const value = issue.value === undefined ? '' : `: “${issue.value}”`;
|
|
238
|
+
return `${issue.part}: “${issue.field}” (${issue.reason}${value})`;
|
|
239
|
+
})
|
|
217
240
|
.join(', ');
|
|
218
241
|
throw new Error(`Invalid search query for “${searchType.name}”: ${detail}.`);
|
|
219
242
|
}
|
package/dist/schema.d.ts
CHANGED
|
@@ -321,6 +321,56 @@ export interface SearchTypeBase {
|
|
|
321
321
|
readonly labelField?: string;
|
|
322
322
|
readonly fields: readonly SearchField[];
|
|
323
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* Which declared field of a {@link RootType} holds its **document key**, when
|
|
326
|
+
* that key is not the node’s own IRI. It has the shape of
|
|
327
|
+
* {@link SearchTypeBase.labelField} – *which field is the label* – and says
|
|
328
|
+
* *which field holds the key*: a statement about the search document, never
|
|
329
|
+
* about the world, so the words a deployment reaches for (identity, alignment,
|
|
330
|
+
* canonical, authority) stay in the deployment’s own schema comments.
|
|
331
|
+
*
|
|
332
|
+
* Two things that already hold of keys then hold here too, without a new rule:
|
|
333
|
+
* several nodes sharing a key are **one document** (the writer upserts by `id`,
|
|
334
|
+
* so a deployment that wants particular content on the merged document attaches
|
|
335
|
+
* a transform, and one that does not gets last-writer-wins), and a reference
|
|
336
|
+
* into a keyed type stores the **target’s key** – a `lookup`/`labelSource`
|
|
337
|
+
* already means *this field holds ids of documents in that collection*
|
|
338
|
+
* (ADR 20), which storing the node IRI would break.
|
|
339
|
+
*
|
|
340
|
+
* See [ADR 22](../../docs/decisions/0022-key-a-root-type-on-a-declared-field.md).
|
|
341
|
+
*/
|
|
342
|
+
export interface KeyField {
|
|
343
|
+
/**
|
|
344
|
+
* The `name` of a declared field of this type whose values are the key
|
|
345
|
+
* candidates: a path-bearing, `array` {@link ReferenceField} that is not
|
|
346
|
+
* `inline` ({@link searchSchema} validates all four). Being an ordinary field
|
|
347
|
+
* is the point – its extraction branch already exists, a reader transform
|
|
348
|
+
* that repairs reference values covers it the day it is declared, and its own
|
|
349
|
+
* {@link ReferenceField.transform} is where IRI normalisation lives, so two
|
|
350
|
+
* spellings of one IRI become one candidate before anything chooses among
|
|
351
|
+
* them.
|
|
352
|
+
*
|
|
353
|
+
* A transform that **replaces** a root’s quads must re-emit this field, the
|
|
354
|
+
* existing rule that *a field the document needs must be in the stream*
|
|
355
|
+
* applied to one more field; a transform that only adds never meets it.
|
|
356
|
+
*/
|
|
357
|
+
readonly field: string;
|
|
358
|
+
/**
|
|
359
|
+
* Choose the key among the candidates – the key field’s values after its
|
|
360
|
+
* `transform`, IRI-filtered, deduplicated and sorted, so the default is
|
|
361
|
+
* deterministic whatever order the CONSTRUCT returned them in. Return one of
|
|
362
|
+
* them, or `undefined` to keep the node’s own IRI; anything else throws at
|
|
363
|
+
* projection. Not consulted for a node whose key field is empty.
|
|
364
|
+
*
|
|
365
|
+
* Must be **pure**: the same function keys the document and every reference
|
|
366
|
+
* to it, so a `pick` that consulted the network or the clock could key the
|
|
367
|
+
* two differently and leave a reference dangling. LDE never inspects an
|
|
368
|
+
* IRI’s shape – it asks.
|
|
369
|
+
*
|
|
370
|
+
* @default the first candidate
|
|
371
|
+
*/
|
|
372
|
+
readonly pick?: (candidates: readonly string[]) => string | undefined;
|
|
373
|
+
}
|
|
324
374
|
/**
|
|
325
375
|
* A **Root Type**: a {@link SearchType} that is indexed. It declares a `class`,
|
|
326
376
|
* roots are selected for it, a Writer owns a collection for it, and the
|
|
@@ -333,6 +383,9 @@ export interface RootType extends SearchTypeBase {
|
|
|
333
383
|
* key a {@link SearchSchema} maps this type under. Its presence is what makes
|
|
334
384
|
* a type a Root Type – and so what gives it a collection. */
|
|
335
385
|
readonly class: string;
|
|
386
|
+
/** Which declared field holds this type’s document key, when it is not the
|
|
387
|
+
* node’s own IRI ({@link KeyField}, {@link documentKeyOf}). */
|
|
388
|
+
readonly key?: KeyField;
|
|
336
389
|
}
|
|
337
390
|
/**
|
|
338
391
|
* A **Reference Type**: a {@link SearchType} reached only through an
|
|
@@ -350,6 +403,10 @@ export interface ReferenceType extends SearchTypeBase {
|
|
|
350
403
|
* {@link SearchField} discriminates by `kind`: an indexed Reference Type
|
|
351
404
|
* fails to compile, not at run time. */
|
|
352
405
|
readonly class?: never;
|
|
406
|
+
/** A Reference Type has no document key to declare: it is nested inside its
|
|
407
|
+
* referrer rather than keyed in a collection of its own. `never` for the
|
|
408
|
+
* same reason `class` is. */
|
|
409
|
+
readonly key?: never;
|
|
353
410
|
}
|
|
354
411
|
/**
|
|
355
412
|
* One type’s complete search declaration: its logical API `name`, the queryable
|
|
@@ -420,6 +477,19 @@ export declare function searchSchema<const Types extends readonly SearchType[]>(
|
|
|
420
477
|
* resolves an inline reference’s referent shape through it.
|
|
421
478
|
*/
|
|
422
479
|
export declare function referenceTypeNamed(schema: SearchSchema, name: string): ReferenceType | undefined;
|
|
480
|
+
/**
|
|
481
|
+
* The {@link RootType} a declaration names – a `lookup`’s `target`, an
|
|
482
|
+
* `idOnly`’s {@link ReferenceField.labelSource} ({@link labelSourceNameOf}), a
|
|
483
|
+
* join edge – or `undefined` when the schema declares no Root Type by that
|
|
484
|
+
* name. The one reading of *which type does this point at*, so the projection
|
|
485
|
+
* (which re-keys a reference through its target’s {@link KeyField}), the
|
|
486
|
+
* label-source validation and an adapter’s lookup cannot resolve a target
|
|
487
|
+
* differently.
|
|
488
|
+
*
|
|
489
|
+
* Complements {@link referenceTypeNamed}: the two name indexes are disjoint,
|
|
490
|
+
* because {@link searchSchema} rejects a name declared twice.
|
|
491
|
+
*/
|
|
492
|
+
export declare function rootTypeNamed(schema: SearchSchema, name: string): RootType | undefined;
|
|
423
493
|
/** Whether a field is an inline reference – a {@link ReferenceField} whose
|
|
424
494
|
* `ref` carries its referent’s projected fields ({@link ReferenceType}). */
|
|
425
495
|
export declare function isInlineReference(field: SearchField): field is ReferenceField & {
|
|
@@ -495,7 +565,7 @@ export declare function labelSourceNameOf(field: ReferenceField): string | undef
|
|
|
495
565
|
*/
|
|
496
566
|
export interface SearchTypeIssue {
|
|
497
567
|
readonly field: string;
|
|
498
|
-
readonly reason: 'duplicate-field-name' | 'invalid-field-name' | 'unknown-kind' | 'invalid-locale' | 'missing-ref' | 'missing-ref-type-name' | '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' | 'joinable-not-allowed' | 'joinable-without-label-source' | 'joinable-with-inline-ref' | 'reserved-field-name';
|
|
568
|
+
readonly reason: 'duplicate-field-name' | 'invalid-field-name' | 'unknown-kind' | 'invalid-locale' | 'missing-ref' | 'missing-ref-type-name' | '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' | 'joinable-not-allowed' | 'joinable-without-label-source' | 'joinable-with-inline-ref' | 'reserved-field-name' | 'key-field-unknown' | 'key-field-not-reference' | 'key-field-without-path' | 'key-field-not-array' | 'key-field-inline' | 'key-pick-not-a-function' | 'key-not-allowed';
|
|
499
569
|
}
|
|
500
570
|
/**
|
|
501
571
|
* The reserved logical name of a document’s IRI – the one field every indexed
|
|
@@ -565,7 +635,10 @@ export declare const OR_KEY = "or";
|
|
|
565
635
|
* field has exactly one), is not an `inline` reference (a projection value is
|
|
566
636
|
* a bare IRI, with no referent to carry fields from), and no two fields
|
|
567
637
|
* declare the same projection value – which would leave a consumer reading
|
|
568
|
-
* the dataset off a declaration no rule picks between
|
|
638
|
+
* the dataset off a declaration no rule picks between;
|
|
639
|
+
* - a {@link RootType.key} names a declared, `path`-bearing, `array`,
|
|
640
|
+
* non-`inline` `reference` field of this type, and its `pick` is a function
|
|
641
|
+
* ({@link keyIssues}); a {@link ReferenceType} declares no key at all.
|
|
569
642
|
*
|
|
570
643
|
* Pure and total: returns every issue rather than throwing;
|
|
571
644
|
* {@link assertValidSearchType} is the throwing entry point.
|
|
@@ -688,6 +761,38 @@ export declare function referenceFields(searchType: SearchType): readonly Refere
|
|
|
688
761
|
export declare function datasetField(searchType: SearchType): (KeywordField | ReferenceField) | undefined;
|
|
689
762
|
/** Look up a field by its logical name. */
|
|
690
763
|
export declare function fieldNamed(searchType: SearchType, name: string): SearchField | undefined;
|
|
764
|
+
/**
|
|
765
|
+
* The **document key** of one node of `searchType`: what the projection writes
|
|
766
|
+
* as the document’s `id`, and what a reference into this type stores.
|
|
767
|
+
*
|
|
768
|
+
* The whole rule, in one place, so the projection and any transform that needs
|
|
769
|
+
* to know a node’s key read the same answer:
|
|
770
|
+
*
|
|
771
|
+
* 1. a type declaring no {@link RootType.key} keys on `nodeIri` – the node’s own
|
|
772
|
+
* IRI, as every type does today;
|
|
773
|
+
* 2. otherwise the key field’s `rawValues` become **candidates**: its
|
|
774
|
+
* {@link ReferenceField.transform} (where IRI normalisation lives, so two
|
|
775
|
+
* spellings of one IRI become one candidate), then the
|
|
776
|
+
* {@link isAbsoluteIri} filter, then dedupe and sort – so the default is
|
|
777
|
+
* deterministic whatever order the CONSTRUCT returned them in;
|
|
778
|
+
* 3. no candidate keys on `nodeIri`, without consulting {@link KeyField.pick};
|
|
779
|
+
* 4. otherwise {@link KeyField.pick} chooses (defaulting to the first
|
|
780
|
+
* candidate), and `undefined` keeps `nodeIri`.
|
|
781
|
+
*
|
|
782
|
+
* So a key is always either the node’s own IRI or an IRI the graph offered for
|
|
783
|
+
* that node – never one invented in between. A `pick` returning anything else
|
|
784
|
+
* throws, naming the node and the candidates: it is a bug in a pure function
|
|
785
|
+
* that keys both the document and every reference to it, and letting it through
|
|
786
|
+
* would key those two differently.
|
|
787
|
+
*
|
|
788
|
+
* @param nodeIri the node’s own IRI – the key it falls back to
|
|
789
|
+
* @param rawValues the key field’s values **as a reference field reads them** –
|
|
790
|
+
* a node’s `@id` or a bare-string value, before the field’s `transform`. A
|
|
791
|
+
* caller that reads them itself (a transform working on quads) must apply the
|
|
792
|
+
* same rule and skip a literal-valued object, or it will compute a key for a
|
|
793
|
+
* value the projection never saw and key the two differently.
|
|
794
|
+
*/
|
|
795
|
+
export declare function documentKeyOf(searchType: RootType, nodeIri: string, rawValues: readonly string[]): string;
|
|
691
796
|
/**
|
|
692
797
|
* The **IR Alias** predicate for a field: `urn:lde:‹SearchType.name›/‹field.name›`.
|
|
693
798
|
* The extraction CONSTRUCT emits a field’s value under this minted predicate, and
|
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
|
@@ -36,6 +36,17 @@ export function defineSearchType(searchType) {
|
|
|
36
36
|
* through {@link referenceTypeNamed}.
|
|
37
37
|
*/
|
|
38
38
|
const referenceTypesBySchema = new WeakMap();
|
|
39
|
+
/**
|
|
40
|
+
* The Root Type name index each {@link SearchSchema} carries alongside its
|
|
41
|
+
* class-keyed map. A schema is keyed by `class` because that is what a selector
|
|
42
|
+
* and a Writer address it by, while every declaration that points at another
|
|
43
|
+
* type – a `lookup`’s `target`, an `idOnly`’s `labelSource`, a join edge –
|
|
44
|
+
* names it. Kept beside the map rather than rebuilt per consumer, so the
|
|
45
|
+
* projection, the label-source validation and an adapter’s lookup resolve a
|
|
46
|
+
* target the same way ({@link rootTypeNamed}) instead of each maintaining a
|
|
47
|
+
* by-name map of its own.
|
|
48
|
+
*/
|
|
49
|
+
const rootTypesBySchema = new WeakMap();
|
|
39
50
|
/** Whether a declared type is a {@link RootType} (declares a `class`) rather
|
|
40
51
|
* than a {@link ReferenceType}. */
|
|
41
52
|
function isRootType(searchType) {
|
|
@@ -80,12 +91,16 @@ export function searchSchema(...types) {
|
|
|
80
91
|
.map((searchType) => [searchType.name, searchType]));
|
|
81
92
|
assertResolvableInlineReferences(types, referenceTypes);
|
|
82
93
|
assertServiceableNestedFields(referenceTypes);
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
94
|
+
// The one blessed cast: only this validated constructor mints the brand. Built
|
|
95
|
+
// BEFORE the schema-wide assertions that resolve a type by name, so they can
|
|
96
|
+
// use `rootTypeNamed` – the same reading the projection and an adapter's
|
|
97
|
+
// lookup use – rather than a by-name map of their own. The schema stays local
|
|
98
|
+
// until every assertion has passed, so an invalid one is never observable.
|
|
99
|
+
const rootTypes = types.filter(isRootType);
|
|
100
|
+
const schema = new Map(rootTypes.map((searchType) => [searchType.class, searchType]));
|
|
88
101
|
referenceTypesBySchema.set(schema, referenceTypes);
|
|
102
|
+
rootTypesBySchema.set(schema, new Map(rootTypes.map((searchType) => [searchType.name, searchType])));
|
|
103
|
+
assertResolvableLabelSources(types, schema);
|
|
89
104
|
// Build the join graph eagerly and discard it: it is cached per schema, and
|
|
90
105
|
// building it is what enforces the schema-wide join rules (one joinable
|
|
91
106
|
// reference per target, every target an indexed Root Type, no cycles). A
|
|
@@ -103,6 +118,21 @@ export function searchSchema(...types) {
|
|
|
103
118
|
export function referenceTypeNamed(schema, name) {
|
|
104
119
|
return referenceTypesBySchema.get(schema)?.get(name);
|
|
105
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* The {@link RootType} a declaration names – a `lookup`’s `target`, an
|
|
123
|
+
* `idOnly`’s {@link ReferenceField.labelSource} ({@link labelSourceNameOf}), a
|
|
124
|
+
* join edge – or `undefined` when the schema declares no Root Type by that
|
|
125
|
+
* name. The one reading of *which type does this point at*, so the projection
|
|
126
|
+
* (which re-keys a reference through its target’s {@link KeyField}), the
|
|
127
|
+
* label-source validation and an adapter’s lookup cannot resolve a target
|
|
128
|
+
* differently.
|
|
129
|
+
*
|
|
130
|
+
* Complements {@link referenceTypeNamed}: the two name indexes are disjoint,
|
|
131
|
+
* because {@link searchSchema} rejects a name declared twice.
|
|
132
|
+
*/
|
|
133
|
+
export function rootTypeNamed(schema, name) {
|
|
134
|
+
return rootTypesBySchema.get(schema)?.get(name);
|
|
135
|
+
}
|
|
106
136
|
/** Whether a field is an inline reference – a {@link ReferenceField} whose
|
|
107
137
|
* `ref` carries its referent’s projected fields ({@link ReferenceType}). */
|
|
108
138
|
export function isInlineReference(field) {
|
|
@@ -311,13 +341,13 @@ export function labelSourceNameOf(field) {
|
|
|
311
341
|
* can actually serve labels ({@link labelFieldOf}). Checked schema-wide,
|
|
312
342
|
* because a single declaration cannot see its siblings.
|
|
313
343
|
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
344
|
+
* Resolved through {@link rootTypeNamed}, which is exactly the set that can
|
|
345
|
+
* serve labels: a label field is `searchable`, and
|
|
346
|
+
* {@link assertServiceableNestedFields} – which runs first – already rejects a
|
|
347
|
+
* `searchable` field on a Reference Type, so a resolved label always has a
|
|
348
|
+
* collection to come from.
|
|
318
349
|
*/
|
|
319
|
-
function assertResolvableLabelSources(types) {
|
|
320
|
-
const byName = new Map(types.map((searchType) => [searchType.name, searchType]));
|
|
350
|
+
function assertResolvableLabelSources(types, schema) {
|
|
321
351
|
for (const searchType of types) {
|
|
322
352
|
for (const field of searchType.fields) {
|
|
323
353
|
const labelSource = field
|
|
@@ -339,9 +369,15 @@ function assertResolvableLabelSources(types) {
|
|
|
339
369
|
if (sourceName === undefined) {
|
|
340
370
|
continue;
|
|
341
371
|
}
|
|
342
|
-
const source =
|
|
372
|
+
const source = rootTypeNamed(schema, sourceName);
|
|
343
373
|
if (source === undefined) {
|
|
344
|
-
|
|
374
|
+
// A name that IS declared, just not as a Root Type, is the confusing
|
|
375
|
+
// case: telling the author to declare a type they already declared
|
|
376
|
+
// would send them looking in the wrong place. Only a Root Type has a
|
|
377
|
+
// collection to resolve against, so name that instead.
|
|
378
|
+
throw new Error(referenceTypeNamed(schema, sourceName) === undefined
|
|
379
|
+
? `Reference “${searchType.name}.${field.name}” names unknown label source “${sourceName}”; declare a SearchType with that name.`
|
|
380
|
+
: `Reference “${searchType.name}.${field.name}” names label source “${sourceName}”, which is a Reference Type; a label source must be a Root Type, since a resolved label is read from that type’s own collection.`);
|
|
345
381
|
}
|
|
346
382
|
if (labelFieldOf(source) === undefined) {
|
|
347
383
|
throw new Error(`Reference “${searchType.name}.${field.name}” uses label source “${sourceName}”, which must declare an output, searchable text field “${labelFieldNameOf(source)}”.`);
|
|
@@ -449,7 +485,10 @@ const LOCALE_PATTERN = /^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$/;
|
|
|
449
485
|
* field has exactly one), is not an `inline` reference (a projection value is
|
|
450
486
|
* a bare IRI, with no referent to carry fields from), and no two fields
|
|
451
487
|
* declare the same projection value – which would leave a consumer reading
|
|
452
|
-
* the dataset off a declaration no rule picks between
|
|
488
|
+
* the dataset off a declaration no rule picks between;
|
|
489
|
+
* - a {@link RootType.key} names a declared, `path`-bearing, `array`,
|
|
490
|
+
* non-`inline` `reference` field of this type, and its `pick` is a function
|
|
491
|
+
* ({@link keyIssues}); a {@link ReferenceType} declares no key at all.
|
|
453
492
|
*
|
|
454
493
|
* Pure and total: returns every issue rather than throwing;
|
|
455
494
|
* {@link assertValidSearchType} is the throwing entry point.
|
|
@@ -596,6 +635,58 @@ export function validateSearchType(searchType) {
|
|
|
596
635
|
}
|
|
597
636
|
}
|
|
598
637
|
}
|
|
638
|
+
issues.push(...keyIssues(searchType));
|
|
639
|
+
return issues;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* The issues a type’s {@link RootType.key} declaration carries, each filed
|
|
643
|
+
* under the field name the declaration names – so a message reads like every
|
|
644
|
+
* other one, naming the field the rule is about rather than a member.
|
|
645
|
+
*
|
|
646
|
+
* The key field must be an ordinary declared field the extraction already
|
|
647
|
+
* reads and the projection already applies a `transform` to: a `reference`
|
|
648
|
+
* (candidates are IRIs), with a `path` (a key read from the graph, not one
|
|
649
|
+
* derived from a document that is keyed already), `array` (a node may offer
|
|
650
|
+
* several candidates, and a single-valued field would drop all but the first
|
|
651
|
+
* before `pick` ever saw them), and not `inline` (which carries the referent’s
|
|
652
|
+
* fields rather than its IRI, so it offers no candidate at all).
|
|
653
|
+
*/
|
|
654
|
+
function keyIssues(searchType) {
|
|
655
|
+
const key = searchType.key;
|
|
656
|
+
if (key === undefined) {
|
|
657
|
+
return [];
|
|
658
|
+
}
|
|
659
|
+
const issue = (reason) => ({
|
|
660
|
+
field: key.field,
|
|
661
|
+
reason,
|
|
662
|
+
});
|
|
663
|
+
// A Reference Type is nested inside its referrer, never keyed in a collection
|
|
664
|
+
// of its own – so a key on one states a rule nothing could apply.
|
|
665
|
+
if (searchType.class === undefined) {
|
|
666
|
+
return [issue('key-not-allowed')];
|
|
667
|
+
}
|
|
668
|
+
const issues = [];
|
|
669
|
+
if (key.pick !== undefined && typeof key.pick !== 'function') {
|
|
670
|
+
issues.push(issue('key-pick-not-a-function'));
|
|
671
|
+
}
|
|
672
|
+
const field = fieldNamed(searchType, key.field);
|
|
673
|
+
if (field === undefined) {
|
|
674
|
+
issues.push(issue('key-field-unknown'));
|
|
675
|
+
return issues;
|
|
676
|
+
}
|
|
677
|
+
if (field.kind !== 'reference') {
|
|
678
|
+
issues.push(issue('key-field-not-reference'));
|
|
679
|
+
return issues;
|
|
680
|
+
}
|
|
681
|
+
if (field.path === undefined) {
|
|
682
|
+
issues.push(issue('key-field-without-path'));
|
|
683
|
+
}
|
|
684
|
+
if (field.array !== true) {
|
|
685
|
+
issues.push(issue('key-field-not-array'));
|
|
686
|
+
}
|
|
687
|
+
if (field.ref?.strategy === 'inline') {
|
|
688
|
+
issues.push(issue('key-field-inline'));
|
|
689
|
+
}
|
|
599
690
|
return issues;
|
|
600
691
|
}
|
|
601
692
|
/**
|
|
@@ -750,6 +841,66 @@ export function datasetField(searchType) {
|
|
|
750
841
|
export function fieldNamed(searchType, name) {
|
|
751
842
|
return searchType.fields.find((field) => field.name === name);
|
|
752
843
|
}
|
|
844
|
+
/**
|
|
845
|
+
* The **document key** of one node of `searchType`: what the projection writes
|
|
846
|
+
* as the document’s `id`, and what a reference into this type stores.
|
|
847
|
+
*
|
|
848
|
+
* The whole rule, in one place, so the projection and any transform that needs
|
|
849
|
+
* to know a node’s key read the same answer:
|
|
850
|
+
*
|
|
851
|
+
* 1. a type declaring no {@link RootType.key} keys on `nodeIri` – the node’s own
|
|
852
|
+
* IRI, as every type does today;
|
|
853
|
+
* 2. otherwise the key field’s `rawValues` become **candidates**: its
|
|
854
|
+
* {@link ReferenceField.transform} (where IRI normalisation lives, so two
|
|
855
|
+
* spellings of one IRI become one candidate), then the
|
|
856
|
+
* {@link isAbsoluteIri} filter, then dedupe and sort – so the default is
|
|
857
|
+
* deterministic whatever order the CONSTRUCT returned them in;
|
|
858
|
+
* 3. no candidate keys on `nodeIri`, without consulting {@link KeyField.pick};
|
|
859
|
+
* 4. otherwise {@link KeyField.pick} chooses (defaulting to the first
|
|
860
|
+
* candidate), and `undefined` keeps `nodeIri`.
|
|
861
|
+
*
|
|
862
|
+
* So a key is always either the node’s own IRI or an IRI the graph offered for
|
|
863
|
+
* that node – never one invented in between. A `pick` returning anything else
|
|
864
|
+
* throws, naming the node and the candidates: it is a bug in a pure function
|
|
865
|
+
* that keys both the document and every reference to it, and letting it through
|
|
866
|
+
* would key those two differently.
|
|
867
|
+
*
|
|
868
|
+
* @param nodeIri the node’s own IRI – the key it falls back to
|
|
869
|
+
* @param rawValues the key field’s values **as a reference field reads them** –
|
|
870
|
+
* a node’s `@id` or a bare-string value, before the field’s `transform`. A
|
|
871
|
+
* caller that reads them itself (a transform working on quads) must apply the
|
|
872
|
+
* same rule and skip a literal-valued object, or it will compute a key for a
|
|
873
|
+
* value the projection never saw and key the two differently.
|
|
874
|
+
*/
|
|
875
|
+
export function documentKeyOf(searchType, nodeIri, rawValues) {
|
|
876
|
+
const key = searchType.key;
|
|
877
|
+
if (key === undefined) {
|
|
878
|
+
return nodeIri;
|
|
879
|
+
}
|
|
880
|
+
// Guaranteed a declared reference field by `searchSchema`, which is what
|
|
881
|
+
// validates a `key` at all.
|
|
882
|
+
const { transform } = fieldNamed(searchType, key.field);
|
|
883
|
+
const candidates = [
|
|
884
|
+
...new Set((transform === undefined ? rawValues : rawValues.map(transform)).filter(isAbsoluteIri)),
|
|
885
|
+
].sort();
|
|
886
|
+
if (candidates.length === 0) {
|
|
887
|
+
return nodeIri;
|
|
888
|
+
}
|
|
889
|
+
const picked = (key.pick ?? firstCandidate)(candidates);
|
|
890
|
+
if (picked === undefined) {
|
|
891
|
+
return nodeIri;
|
|
892
|
+
}
|
|
893
|
+
if (!candidates.includes(picked)) {
|
|
894
|
+
throw new Error(`The “pick” of “${searchType.name}.key” returned “${picked}” for <${nodeIri}>, which is not among its candidates (${candidates
|
|
895
|
+
.map((candidate) => `<${candidate}>`)
|
|
896
|
+
.join(', ')}); a key must be one of them, or “undefined” to keep the node’s own IRI.`);
|
|
897
|
+
}
|
|
898
|
+
return picked;
|
|
899
|
+
}
|
|
900
|
+
/** The {@link KeyField.pick} a type that declares none falls back to. */
|
|
901
|
+
function firstCandidate(candidates) {
|
|
902
|
+
return candidates[0];
|
|
903
|
+
}
|
|
753
904
|
/**
|
|
754
905
|
* The **IR Alias** predicate for a field: `urn:lde:‹SearchType.name›/‹field.name›`.
|
|
755
906
|
* The extraction CONSTRUCT emits a field’s value under this minted predicate, and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.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",
|