@lde/search 0.21.0 → 0.22.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 +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/join-graph.d.ts.map +1 -1
- package/dist/join-graph.js +8 -7
- package/dist/project.js +12 -2
- package/dist/query.d.ts +35 -5
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +49 -3
- package/dist/schema.d.ts +68 -57
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +53 -21
- 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, 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, inlineFramingDepth, fieldNamed, datasetField, ID_FIELD, AND_KEY, OR_KEY, labelFieldOf, labelFieldNameOf, labelSourceNameOf, 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,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,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"}
|
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, 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, inlineFramingDepth, fieldNamed, datasetField, ID_FIELD, AND_KEY, OR_KEY, labelFieldOf, labelFieldNameOf, labelSourceNameOf, 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,11 +1,11 @@
|
|
|
1
1
|
export { projectRoots } from './project.js';
|
|
2
2
|
export type { ProjectedNode, ProjectionContext, SearchDocument, } from './project.js';
|
|
3
|
-
export { defineSearchType, searchSchema, validateSearchType, assertValidSearchType, ID_FIELD, AND_KEY, OR_KEY, } 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';
|
|
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';
|
|
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';
|
|
8
|
-
export type { SearchQuery, Filter, Criterion, CriterionBase, Sort, } from './query.js';
|
|
8
|
+
export type { SearchQuery, ReferenceProjection, Filter, Criterion, CriterionBase, Sort, } from './query.js';
|
|
9
9
|
export type { SearchEngine, SearchResult, SearchHit, ResultDocument, SearchValue, LocalizedValue, Reference, NestedDocument, FacetBucket, FacetMap, FacetsOutcome, FacetFieldsOf, OutputFieldsOf, } from './engine.js';
|
|
10
10
|
export type { FramedNode } from './frame-by-type.js';
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
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,
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,13 @@ export { projectRoots } from './project.js';
|
|
|
10
10
|
// Unified field model: one declaration drives projection, engine collection
|
|
11
11
|
// schema, query semantics and the GraphQL surface – a discriminated union by
|
|
12
12
|
// `kind`, validated again at runtime when the schema is built.
|
|
13
|
-
export { defineSearchType, searchSchema, validateSearchType, assertValidSearchType, ID_FIELD, AND_KEY, OR_KEY,
|
|
13
|
+
export { defineSearchType, searchSchema, validateSearchType, assertValidSearchType, ID_FIELD, AND_KEY, OR_KEY,
|
|
14
|
+
// The `date` storage codec. A schema author needs it whenever a value
|
|
15
|
+
// bypasses the projection’s own conversion – a `date` populated outside the
|
|
16
|
+
// projection, or a derive that computes seconds from something other than an
|
|
17
|
+
// ISO string – so it belongs on the authoring surface, not only the adapter
|
|
18
|
+
// one.
|
|
19
|
+
isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
14
20
|
// The declared join edges between root types: which types a query can filter
|
|
15
21
|
// across, and which collections must be rebuilt together. Built eagerly by
|
|
16
22
|
// `searchSchema`, so declaring a schema is what validates the joins.
|
package/dist/join-graph.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"join-graph.d.ts","sourceRoot":"","sources":["../src/join-graph.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"join-graph.d.ts","sourceRoot":"","sources":["../src/join-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAErB;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC,SAAS,QAAQ,EAAE,CAAC,EAAE,CAAC;IACtD;;;;;;OAMG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC1E;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CAQzD"}
|
package/dist/join-graph.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { referenceFields, } from './schema.js';
|
|
1
|
+
import { labelSourceNameOf, referenceFields, } from './schema.js';
|
|
2
2
|
/**
|
|
3
3
|
* The deepest `on` path a query may state: `dataset → publisher → country` is
|
|
4
4
|
* three hops. Fixed rather than configurable, and enforced in the query IR
|
|
@@ -97,12 +97,13 @@ function declaredEdges(searchType, byName) {
|
|
|
97
97
|
if (field.joinable !== true) {
|
|
98
98
|
continue;
|
|
99
99
|
}
|
|
100
|
-
// Both lookups always hit: `validateSearchType` rejects `joinable`
|
|
101
|
-
//
|
|
102
|
-
// must declare a `searchable`
|
|
103
|
-
// carry (a nested field is `output`
|
|
104
|
-
// collection at the far end without
|
|
105
|
-
|
|
100
|
+
// Both lookups always hit: `validateSearchType` rejects `joinable` on a
|
|
101
|
+
// reference that names no type to resolve against, and such a type is
|
|
102
|
+
// always an indexed Root Type – it must declare a `searchable` label
|
|
103
|
+
// field, which a Reference Type cannot carry (a nested field is `output`
|
|
104
|
+
// only). So a joinable edge always has a collection at the far end without
|
|
105
|
+
// a rule of its own.
|
|
106
|
+
const target = byName.get(labelSourceNameOf(field));
|
|
106
107
|
const claimed = claimedBy.get(target.name);
|
|
107
108
|
if (claimed !== undefined) {
|
|
108
109
|
throw new Error(`Search type “${searchType.name}” declares two joinable references to “${target.name}” (“${claimed}” and “${field.name}”); an engine addresses a join by collection, not by field, so only one reference per target can be joinable. Drop “joinable” from one of them – it keeps its labels, facets and id filtering.`);
|
package/dist/project.js
CHANGED
|
@@ -130,8 +130,18 @@ function applyField(document, node, field, searchType, schema, context) {
|
|
|
130
130
|
// returning `undefined` does.
|
|
131
131
|
const value = field.kind === 'reference' && derived !== undefined
|
|
132
132
|
? derivedIris(derived)
|
|
133
|
-
: derived
|
|
134
|
-
|
|
133
|
+
: // A derived `date` goes through the same storage codec as a read one:
|
|
134
|
+
// the field is stored as Unix seconds, so an ISO string returned here
|
|
135
|
+
// would otherwise land as a string in an int64 field. A derive that
|
|
136
|
+
// already computed seconds returns a number and passes through.
|
|
137
|
+
field.kind === 'date' && typeof derived === 'string'
|
|
138
|
+
? isoToUnixSeconds(derived)
|
|
139
|
+
: derived;
|
|
140
|
+
// NaN is not a value any kind stores, and a derive is the only route that
|
|
141
|
+
// can produce one (`setNumber` guards the read route). It serializes as
|
|
142
|
+
// `null`, which an engine rejects for a numeric field – so drop it, leaving
|
|
143
|
+
// the field absent as an unparseable string already does.
|
|
144
|
+
if (value !== undefined && !Number.isNaN(value)) {
|
|
135
145
|
document[field.name] = value;
|
|
136
146
|
}
|
|
137
147
|
return;
|
package/dist/query.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SearchType } from './schema.js';
|
|
1
|
+
import { type SearchSchema, type SearchType } from './schema.js';
|
|
2
2
|
import { type JoinGraph } from './join-graph.js';
|
|
3
3
|
/**
|
|
4
4
|
* The engine- and protocol-neutral query IR. Every API surface compiles its
|
|
@@ -20,6 +20,16 @@ export interface SearchQuery {
|
|
|
20
20
|
readonly facets: readonly string[];
|
|
21
21
|
/** Selects the per-locale fields to query/sort on (from `Accept-Language`). */
|
|
22
22
|
readonly locale: string;
|
|
23
|
+
/**
|
|
24
|
+
* Which `lookup` references to resolve on the hits, and how much of each
|
|
25
|
+
* referent to carry. Omitted (or a field left out of it), a lookup resolves
|
|
26
|
+
* its target’s label alone – what every reference carried before.
|
|
27
|
+
*
|
|
28
|
+
* A surface builds this from what its caller asked for (GraphQL: the
|
|
29
|
+
* selection set), so the engine fetches neither less nor more. See
|
|
30
|
+
* [ADR 20](../../docs/decisions/0020-resolve-a-references-fields-from-the-targets-own-collection.md).
|
|
31
|
+
*/
|
|
32
|
+
readonly resolve?: ReferenceProjection;
|
|
23
33
|
}
|
|
24
34
|
/**
|
|
25
35
|
* What every {@link Criterion} carries, whatever its operator: the field it
|
|
@@ -50,6 +60,19 @@ export interface CriterionBase {
|
|
|
50
60
|
*/
|
|
51
61
|
readonly on?: readonly string[];
|
|
52
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* What to carry for each resolved reference, keyed by the reference field’s
|
|
65
|
+
* name. `fields` names the referent’s own output fields; `resolve` nests, for a
|
|
66
|
+
* referent whose own references are wanted in turn – an engine answers one
|
|
67
|
+
* batched lookup per level, never one per document.
|
|
68
|
+
*
|
|
69
|
+
* Deliberately the shape of a selection set rather than a second vocabulary: a
|
|
70
|
+
* per-level option (a `limit` here, a `where` there) would multiply with depth.
|
|
71
|
+
*/
|
|
72
|
+
export type ReferenceProjection = Readonly<Record<string, {
|
|
73
|
+
readonly fields?: readonly string[];
|
|
74
|
+
readonly resolve?: ReferenceProjection;
|
|
75
|
+
}>>;
|
|
53
76
|
/**
|
|
54
77
|
* One criterion on one field. The operator is fixed by that field’s
|
|
55
78
|
* {@link FieldKind} ({@link filterOperatorFor}): keyword/reference use `in` (OR
|
|
@@ -139,14 +162,16 @@ export declare function isUnsatisfiable(query: SearchQuery): boolean;
|
|
|
139
162
|
* clause with no criteria) are NOT issues – a compiler skips those as no-ops.
|
|
140
163
|
*/
|
|
141
164
|
export interface QueryIssue {
|
|
142
|
-
readonly part: 'where' | 'facets' | 'orderBy';
|
|
165
|
+
readonly part: 'where' | 'facets' | 'orderBy' | 'resolve';
|
|
143
166
|
readonly field: string;
|
|
144
167
|
readonly reason: 'unknown-field' | 'not-filterable' | 'operator-mismatch' | 'not-facetable'
|
|
145
168
|
/** The criterion’s `on` path is longer than {@link MAX_JOIN_DEPTH}. */
|
|
146
169
|
| 'join-too-deep'
|
|
147
170
|
/** The `on` path does not resolve: a name that is not a field, a reference
|
|
148
171
|
* that is not `joinable`, or no join graph to resolve it against. */
|
|
149
|
-
| 'unknown-join'
|
|
172
|
+
| 'unknown-join'
|
|
173
|
+
/** A projected reference is not a `lookup`, so nothing resolves it. */
|
|
174
|
+
| 'not-resolvable';
|
|
150
175
|
}
|
|
151
176
|
/**
|
|
152
177
|
* Structurally validate a query against its search type: **every criterion of
|
|
@@ -168,15 +193,20 @@ export interface QueryIssue {
|
|
|
168
193
|
* with no join path needs none; a joined criterion validated without one is an
|
|
169
194
|
* `unknown-join`, never a silently unchecked filter.
|
|
170
195
|
*
|
|
196
|
+
* A `resolve` projection is checked at every level, which is why the whole
|
|
197
|
+
* `schema` is passed alongside the type being queried: each level’s fields
|
|
198
|
+
* belong to the `target` the level above names, and only the schema resolves a
|
|
199
|
+
* target to its type.
|
|
200
|
+
*
|
|
171
201
|
* This is the port’s always-on guard: every {@link SearchEngine} adapter MUST
|
|
172
202
|
* reject a query with issues ({@link assertValidQuery}) instead of passing
|
|
173
203
|
* garbage to its engine, so validation holds for every caller – including
|
|
174
204
|
* `queryDefaults` policies and surfaces weaker than GraphQL.
|
|
175
205
|
*/
|
|
176
|
-
export declare function validateQuery(query: SearchQuery, searchType: SearchType, joins?: JoinGraph): readonly QueryIssue[];
|
|
206
|
+
export declare function validateQuery(query: SearchQuery, searchType: SearchType, schema: SearchSchema, joins?: JoinGraph): readonly QueryIssue[];
|
|
177
207
|
/** Throw on the first structurally invalid query part ({@link validateQuery}),
|
|
178
208
|
* naming every issue. The always-on entry point for engine adapters. */
|
|
179
|
-
export declare function assertValidQuery(query: SearchQuery, searchType: SearchType, joins?: JoinGraph): void;
|
|
209
|
+
export declare function assertValidQuery(query: SearchQuery, searchType: SearchType, schema: SearchSchema, joins?: JoinGraph): void;
|
|
180
210
|
/**
|
|
181
211
|
* The 1-based page an `offset` falls on – the numbered-pagination presentation
|
|
182
212
|
* of the IR, shared by the surfaces and the adapters. `limit: 0` (a facet-only
|
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,EAIL,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,SAAS,IAAI,EAAE,CAAC;IAClC,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|
package/dist/query.js
CHANGED
|
@@ -77,12 +77,17 @@ function issueField(criterion) {
|
|
|
77
77
|
* with no join path needs none; a joined criterion validated without one is an
|
|
78
78
|
* `unknown-join`, never a silently unchecked filter.
|
|
79
79
|
*
|
|
80
|
+
* A `resolve` projection is checked at every level, which is why the whole
|
|
81
|
+
* `schema` is passed alongside the type being queried: each level’s fields
|
|
82
|
+
* belong to the `target` the level above names, and only the schema resolves a
|
|
83
|
+
* target to its type.
|
|
84
|
+
*
|
|
80
85
|
* This is the port’s always-on guard: every {@link SearchEngine} adapter MUST
|
|
81
86
|
* reject a query with issues ({@link assertValidQuery}) instead of passing
|
|
82
87
|
* garbage to its engine, so validation holds for every caller – including
|
|
83
88
|
* `queryDefaults` policies and surfaces weaker than GraphQL.
|
|
84
89
|
*/
|
|
85
|
-
export function validateQuery(query, searchType, joins) {
|
|
90
|
+
export function validateQuery(query, searchType, schema, joins) {
|
|
86
91
|
const issues = [];
|
|
87
92
|
for (const filter of query.where) {
|
|
88
93
|
// Each criterion is checked on its own, so a multi-criterion clause needs
|
|
@@ -149,6 +154,7 @@ export function validateQuery(query, searchType, joins) {
|
|
|
149
154
|
issues.push({ part: 'facets', field: name, reason: 'not-facetable' });
|
|
150
155
|
}
|
|
151
156
|
}
|
|
157
|
+
collectProjectionIssues(query.resolve, searchType, schema, issues);
|
|
152
158
|
for (const sort of query.orderBy) {
|
|
153
159
|
if (sort.field !== 'relevance' &&
|
|
154
160
|
fieldNamed(searchType, sort.field) === undefined) {
|
|
@@ -161,10 +167,50 @@ export function validateQuery(query, searchType, joins) {
|
|
|
161
167
|
}
|
|
162
168
|
return issues;
|
|
163
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Walk a projection level by level: each key names a `lookup` reference on the
|
|
172
|
+
* type that level belongs to, each `fields` entry an `output` field of the
|
|
173
|
+
* target it resolves against, and each nested `resolve` repeats against that
|
|
174
|
+
* target. A level whose reference is unresolvable stops there – its subtree
|
|
175
|
+
* belongs to a target that does not exist, so reporting it too would bury the
|
|
176
|
+
* one mistake under its consequences.
|
|
177
|
+
*/
|
|
178
|
+
function collectProjectionIssues(projection, searchType, schema, issues) {
|
|
179
|
+
for (const [name, level] of Object.entries(projection ?? {})) {
|
|
180
|
+
const field = fieldNamed(searchType, name);
|
|
181
|
+
if (field === undefined) {
|
|
182
|
+
issues.push({ part: 'resolve', field: name, reason: 'unknown-field' });
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (field.kind !== 'reference' || field.ref?.strategy !== 'lookup') {
|
|
186
|
+
issues.push({ part: 'resolve', field: name, reason: 'not-resolvable' });
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const targetName = field.ref.target;
|
|
190
|
+
const target = [...schema.values()].find((rootType) => rootType.name === targetName);
|
|
191
|
+
if (target === undefined) {
|
|
192
|
+
// searchSchema rejects a lookup whose target it cannot resolve, so this
|
|
193
|
+
// is a query built against a different schema than the engine serves.
|
|
194
|
+
issues.push({ part: 'resolve', field: name, reason: 'not-resolvable' });
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
for (const wanted of level.fields ?? []) {
|
|
198
|
+
const targetField = fieldNamed(target, wanted);
|
|
199
|
+
if (targetField === undefined || targetField.output !== true) {
|
|
200
|
+
issues.push({
|
|
201
|
+
part: 'resolve',
|
|
202
|
+
field: `${name}.${wanted}`,
|
|
203
|
+
reason: 'unknown-field',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
collectProjectionIssues(level.resolve, target, schema, issues);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
164
210
|
/** Throw on the first structurally invalid query part ({@link validateQuery}),
|
|
165
211
|
* naming every issue. The always-on entry point for engine adapters. */
|
|
166
|
-
export function assertValidQuery(query, searchType, joins) {
|
|
167
|
-
const issues = validateQuery(query, searchType, joins);
|
|
212
|
+
export function assertValidQuery(query, searchType, schema, joins) {
|
|
213
|
+
const issues = validateQuery(query, searchType, schema, joins);
|
|
168
214
|
if (issues.length > 0) {
|
|
169
215
|
const detail = issues
|
|
170
216
|
.map((issue) => `${issue.part}: “${issue.field}” (${issue.reason})`)
|
package/dist/schema.d.ts
CHANGED
|
@@ -111,6 +111,12 @@ export interface SearchFieldBase {
|
|
|
111
111
|
* dataset being indexed). It is what lets a derive relate a projected value
|
|
112
112
|
* to its provenance: dropping a polymorphic `isPartOf` value that merely
|
|
113
113
|
* points back at the containing dataset, say.
|
|
114
|
+
*
|
|
115
|
+
* The returned value goes through the same storage conversion a read one
|
|
116
|
+
* does, so a derive states its value in the field’s own terms: a `date`
|
|
117
|
+
* derive may return an ISO 8601 string ({@link isoToUnixSeconds} is applied
|
|
118
|
+
* for it) or the stored Unix seconds directly, and a `reference` derive is
|
|
119
|
+
* held to absolute IRIs.
|
|
114
120
|
*/
|
|
115
121
|
readonly derive?: (document: ProjectedNode, context: ProjectionContext) => unknown;
|
|
116
122
|
}
|
|
@@ -168,7 +174,38 @@ export interface KeywordField extends SearchFieldBase, Searchable {
|
|
|
168
174
|
* exclusive with `path` and `derive`. */
|
|
169
175
|
readonly from?: ProjectionValue;
|
|
170
176
|
}
|
|
171
|
-
/**
|
|
177
|
+
/**
|
|
178
|
+
* Where a reference’s fields come from – the one axis the three shapes sit on:
|
|
179
|
+
*
|
|
180
|
+
* - `idOnly` carries the bare IRI and nothing else. It emits no type, so it
|
|
181
|
+
* names none; a {@link ReferenceField.labelSource} still labels its facet
|
|
182
|
+
* buckets.
|
|
183
|
+
* - `lookup` carries fields read from the **target’s own indexed document**,
|
|
184
|
+
* resolved at query time from that Root Type’s collection. `target` names it
|
|
185
|
+
* once – the collection to read and the emitted type name alike (GraphQL:
|
|
186
|
+
* `‹Target›Reference`, since type names must be unique).
|
|
187
|
+
* - `inline` carries fields denormalised from the **parent’s** RDF framing at
|
|
188
|
+
* index time, shaped by the declared {@link ReferenceType} `typeName` names –
|
|
189
|
+
* so its typeName can never name a root type.
|
|
190
|
+
*/
|
|
191
|
+
export type ReferenceStrategy = {
|
|
192
|
+
readonly strategy: 'idOnly';
|
|
193
|
+
/** Optional, unlike the other strategies – including when the field is
|
|
194
|
+
* `output`: an `idOnly` reference surfaces as its bare IRI, so there is
|
|
195
|
+
* no object type to name. It names the reference’s *filter* target
|
|
196
|
+
* instead, so declare it where the referent’s IRIs form a nameable set a
|
|
197
|
+
* consumer should be able to tell from IRIs at large; omit it for IRIs
|
|
198
|
+
* that belong to no such set. */
|
|
199
|
+
readonly typeName?: string;
|
|
200
|
+
readonly target?: never;
|
|
201
|
+
} | {
|
|
202
|
+
readonly strategy: 'lookup';
|
|
203
|
+
readonly target: string;
|
|
204
|
+
} | {
|
|
205
|
+
readonly strategy: 'inline';
|
|
206
|
+
readonly typeName: string;
|
|
207
|
+
};
|
|
208
|
+
/** An IRI-valued reference to another entity, resolved at the surface. */
|
|
172
209
|
export interface ReferenceField extends SearchFieldBase, Searchable {
|
|
173
210
|
readonly kind: 'reference';
|
|
174
211
|
/** Projection-time value transform. */
|
|
@@ -180,22 +217,23 @@ export interface ReferenceField extends SearchFieldBase, Searchable {
|
|
|
180
217
|
* with `path` and `derive`. */
|
|
181
218
|
readonly from?: ProjectionValue;
|
|
182
219
|
/**
|
|
183
|
-
* The `name` of the
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
220
|
+
* The `name` of the Root Type whose collection labels this reference’s facet
|
|
221
|
+
* buckets. Only an `idOnly` reference declares one: a `lookup` reads its
|
|
222
|
+
* labels from the `target` it already names, and an `inline` reference
|
|
223
|
+
* carries the referent’s own fields. The named type must declare an `output`,
|
|
224
|
+
* `searchable` text field under its {@link SearchTypeBase.labelField} name
|
|
225
|
+
* (`label` by default; validated by {@link searchSchema}), so an engine can
|
|
226
|
+
* both reconstruct the label and search it (typeahead).
|
|
190
227
|
*/
|
|
191
228
|
readonly labelSource?: string;
|
|
192
229
|
/**
|
|
193
230
|
* Turn this reference into an **engine-level join**, so a query can filter
|
|
194
231
|
* this type by a condition on the referent – `“every object published by
|
|
195
|
-
* institution X”` in one round-trip instead of two. Valid only
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
232
|
+
* institution X”` in one round-trip instead of two. Valid only where the
|
|
233
|
+
* reference names the type it resolves against – a `lookup`’s `target` or an
|
|
234
|
+
* `idOnly`’s {@link ReferenceField.labelSource} ({@link labelSourceNameOf}) –
|
|
235
|
+
* which already asserts that this field’s values are ids of documents in that
|
|
236
|
+
* type’s collection, exactly the fact a join needs.
|
|
199
237
|
*
|
|
200
238
|
* A capability flag in the vocabulary of `filterable`/`facetable`/`sortable`,
|
|
201
239
|
* and deliberately not derived from `labelSource`: an engine may refuse to
|
|
@@ -212,54 +250,20 @@ export interface ReferenceField extends SearchFieldBase, Searchable {
|
|
|
212
250
|
*/
|
|
213
251
|
readonly joinable?: boolean;
|
|
214
252
|
/**
|
|
215
|
-
* The referenced entity’s shape and
|
|
216
|
-
* the field is `output` – the strategy is what decides the output shape, so
|
|
217
|
-
*
|
|
253
|
+
* The referenced entity’s shape and where its fields come from. Required when
|
|
254
|
+
* the field is `output` – the strategy is what decides the output shape, so a
|
|
255
|
+
* surfaced reference must state it; optional for a facet- or filter-only
|
|
218
256
|
* reference.
|
|
219
257
|
*
|
|
220
|
-
* A **discriminated union by `strategy
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
* then tell IRIs of that target apart from IRIs at large.
|
|
258
|
+
* A **discriminated union by `strategy`** ({@link ReferenceStrategy}), because
|
|
259
|
+
* what names the referent differs per strategy and is meaningless for the
|
|
260
|
+
* others: a `lookup` names the Root Type it reads from, an `inline` one names
|
|
261
|
+
* a declared {@link ReferenceType}, and an `idOnly` one carries a bare IRI
|
|
262
|
+
* that may belong to no declared type at all (`sameAs` points at a vocabulary
|
|
263
|
+
* nobody indexes). See
|
|
264
|
+
* [ADR 20](../../docs/decisions/0020-resolve-a-references-fields-from-the-targets-own-collection.md).
|
|
228
265
|
*/
|
|
229
|
-
readonly ref?:
|
|
230
|
-
/** Logical API type name of the referenced entity (PascalCase, e.g.
|
|
231
|
-
* `Organization`) – names the reference’s type in the API surfaces,
|
|
232
|
-
* the way {@link SearchType.name} names a root type; fields sharing it
|
|
233
|
-
* share one emitted type. For a `labelOnly` reference it is a name,
|
|
234
|
-
* not a key: it need not correspond to any indexed root type, and it
|
|
235
|
-
* may name one (`creator` → `Person`, with the labels resolved from
|
|
236
|
-
* that root’s collection via `labelSource`) – an API surface then
|
|
237
|
-
* serves the reference under a derived name (GraphQL:
|
|
238
|
-
* `‹Name›Reference`) to keep type names unique. An `inline`
|
|
239
|
-
* reference’s typeName instead resolves to a declared
|
|
240
|
-
* {@link ReferenceType}, so it can never name a root type. */
|
|
241
|
-
readonly typeName: string;
|
|
242
|
-
/** `labelOnly` carries the id plus a display label resolved from a
|
|
243
|
-
* label source; `inline` carries the referent’s own projected fields
|
|
244
|
-
* through a declared {@link ReferenceType} (see
|
|
245
|
-
* {@link isInlineReference}). */
|
|
246
|
-
readonly strategy: 'labelOnly' | 'inline';
|
|
247
|
-
} | {
|
|
248
|
-
/** Optional here, unlike the other strategies: an `idOnly` reference
|
|
249
|
-
* emits no type of its own, so a name is not needed to emit one under.
|
|
250
|
-
* Declare it when the referent’s IRIs form a nameable set an API
|
|
251
|
-
* surface should distinguish; omit it for IRIs that belong to no such
|
|
252
|
-
* set. */
|
|
253
|
-
readonly typeName?: string;
|
|
254
|
-
/** The IRI, and nothing else – no label resolution, no nested fields.
|
|
255
|
-
* Surfaces as a bare IRI rather than an object, which is what makes it
|
|
256
|
-
* the right strategy for a reference whose referent is not an entity
|
|
257
|
-
* this deployment describes: a canonical vocabulary URI, a licence, a
|
|
258
|
-
* content URL. A {@link ReferenceField.labelSource} is still honoured
|
|
259
|
-
* for facet bucket labels – the strategy governs the output shape, not
|
|
260
|
-
* whether a bucket can be labelled. */
|
|
261
|
-
readonly strategy: 'idOnly';
|
|
262
|
-
};
|
|
266
|
+
readonly ref?: ReferenceStrategy;
|
|
263
267
|
}
|
|
264
268
|
/**
|
|
265
269
|
* Range-facet bins for a numeric (`integer`/`number`/`date`) facetable field.
|
|
@@ -476,6 +480,13 @@ export declare function labelFieldNameOf(searchType: SearchType): string;
|
|
|
476
480
|
* named as a {@link ReferenceField.labelSource}.
|
|
477
481
|
*/
|
|
478
482
|
export declare function labelFieldOf(searchType: SearchType): TextField | undefined;
|
|
483
|
+
/**
|
|
484
|
+
* The Root Type a reference resolves labels from, by name: a `lookup`’s
|
|
485
|
+
* `target`, an `idOnly`’s {@link ReferenceField.labelSource}, or `undefined`
|
|
486
|
+
* when it resolves none. One reading for the two declarations, so a consumer
|
|
487
|
+
* never branches on the strategy to find the collection.
|
|
488
|
+
*/
|
|
489
|
+
export declare function labelSourceNameOf(field: ReferenceField): string | undefined;
|
|
479
490
|
/**
|
|
480
491
|
* One structural problem {@link validateSearchType} found: a field declares a
|
|
481
492
|
* capability or property its `kind` cannot honour, or the declaration is
|
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,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGrE;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GACnB,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AAE/E;oEACoE;AACpE,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,CAAC;AAcnD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,cAAc,GAAG,SAAS,CAE7E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,MAAM,WAAW,GACrB,SAAS,GAAG,YAAY,GAAG,cAAc,GAAG,YAAY,GAAG,YAAY,CAAC;AAE1E,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B;wCACoC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;+EAC2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;2DAGuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB;;yCAEqC;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,yBAAyB;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,iCAAiC;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGrE;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GACnB,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AAE/E;oEACoE;AACpE,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,CAAC;AAcnD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,cAAc,GAAG,SAAS,CAE7E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,MAAM,WAAW,GACrB,SAAS,GAAG,YAAY,GAAG,cAAc,GAAG,YAAY,GAAG,YAAY,CAAC;AAE1E,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B;wCACoC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;+EAC2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;2DAGuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB;;yCAEqC;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,yBAAyB;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,iCAAiC;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAChB,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,iBAAiB,KACvB,OAAO,CAAC;CACd;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,CAAC;AAExC;kEACkE;AAClE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACnD;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,SAAU,SAAQ,eAAe,EAAE,UAAU;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC;CAC9B;AAED;gCACgC;AAChC,MAAM,WAAW,YAAa,SAAQ,eAAe,EAAE,UAAU;IAC/D,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC;IAC7B;8CAC0C;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;CACjC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,GACzB;IACE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B;;;;;sCAKkC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC;CACzB,GACD;IAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,0EAA0E;AAC1E,MAAM,WAAW,cAAe,SAAQ,eAAe,EAAE,UAAU;IACjE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC;IAC7B;;;oCAGgC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;IAChC;;;;;;;;OAQG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CAC9C;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAa,SAAQ,eAAe,EAAE,cAAc;IACnE,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC7C,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC;CAC7B;AAED,0DAA0D;AAC1D,MAAM,WAAW,YAAa,SAAQ,eAAe;IACnD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B;;;;+CAI2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;2DAMuD;IACvD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;CACzC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,QAAS,SAAQ,cAAc;IAC9C;;kEAE8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD;;;6CAGyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,aAAa,CAAC;AAElD;;kEAEkE;AAClE,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,SAAS,UAAU,EAAE,IAAI,OAAO,CACnE,KAAK,CAAC,MAAM,CAAC,EACb;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAC3B,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,IAAI,SAAS,UAAU,EAC5D,UAAU,EAAE,IAAI,GACf,IAAI,CAEN;AAED;;;;;;;;;;;GAWG;AACH;;;uCAGuC;AACvC,MAAM,CAAC,OAAO,CAAC,MAAM,iBAAiB,EAAE,OAAO,MAAM,CAAC;AAEtD,MAAM,WAAW,YAAY,CAC3B,KAAK,SAAS,SAAS,UAAU,EAAE,GAAG,SAAS,UAAU,EAAE,CAC3D,SAAQ,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAC9C,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAC;CACpC;AAoBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,KAAK,SAAS,SAAS,UAAU,EAAE,EACpE,GAAG,KAAK,EAAE,KAAK,GACd,YAAY,CAAC,KAAK,CAAC,CA0CrB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,GACX,aAAa,GAAG,SAAS,CAE3B;AAED;6EAC6E;AAC7E,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,GACjB,KAAK,IAAI,cAAc,GAAG;IAC3B,QAAQ,CAAC,GAAG,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;CAC1E,CAEA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,WAAW,GACjB,aAAa,GAAG,SAAS,CAI3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,GACrB,MAAM,CAER;AA8JD;yCACyC;AACzC,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C;uDACuD;AACvD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAE/D;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAQ1E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAI3E;AA0DD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EACX,sBAAsB,GACtB,oBAAoB,GACpB,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,uBAAuB,GACvB,iBAAiB,GACjB,uBAAuB,GACvB,qBAAqB,GACrB,0BAA0B,GAC1B,wBAAwB,GACxB,uBAAuB,GACvB,kBAAkB,GAClB,kBAAkB,GAClB,gBAAgB,GAChB,kBAAkB,GAClB,sBAAsB,GACtB,0BAA0B,GAC1B,4BAA4B,GAC5B,qBAAqB,GACrB,oBAAoB,GACpB,sBAAsB,GACtB,+BAA+B,GAC/B,0BAA0B,GAC1B,qBAAqB,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,QAAQ,OAAO,CAAC;AAE7B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,OAAO,QAAQ,CAAC;AAC7B,eAAO,MAAM,MAAM,OAAO,CAAC;AAyC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,GACrB,SAAS,eAAe,EAAE,CA6J5B;AAcD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,QAAQ,GACnB,IAAI,CAUN;AAED;+EAC+E;AAC/E,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAQlE;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B;sEACkE;IAClE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;4EACwE;IACxE,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAExE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAOpB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,UAAU,GACrB,SAAS,CAAC,WAAW,GAAG;IACzB,QAAQ,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD,CAAC,EAAE,CAOH;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAC7B,UAAU,EAAE,UAAU,GACrB,SAAS,WAAW,EAAE,CAExB;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,UAAU,GACrB,SAAS,WAAW,EAAE,CAExB;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,WAAW,EAAE,CAE7E;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,WAAW,EAAE,CAE3E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAS3D;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAC7B,UAAU,EAAE,UAAU,GACrB,SAAS,cAAc,EAAE,CAE3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,GACrB,CAAC,YAAY,GAAG,cAAc,CAAC,GAAG,SAAS,CAM7C;AAED,2CAA2C;AAC3C,wBAAgB,UAAU,CACxB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GACX,WAAW,GAAG,SAAS,CAEzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,WAAW,GACjB,KAAK,IAAI,YAAY,GAAG;IAAE,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAA;CAAE,CAEzE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAOD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGhE;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,CAgBjE"}
|
package/dist/schema.js
CHANGED
|
@@ -246,6 +246,12 @@ function assertServiceableNestedFields(referenceTypes) {
|
|
|
246
246
|
if (field.labelSource !== undefined) {
|
|
247
247
|
throw new Error(`Nested field “${referenceType.name}.${field.name}” declares a label source, which an inline reference cannot serve; nest the referent through an inline reference instead of resolving a label for it.`);
|
|
248
248
|
}
|
|
249
|
+
// A lookup is resolved from the hit's own projection, level by level; a
|
|
250
|
+
// nested document is read back with its referent and never appears in
|
|
251
|
+
// one. Accepting it would emit a type whose every field serves null.
|
|
252
|
+
if (field.kind === 'reference' && field.ref?.strategy === 'lookup') {
|
|
253
|
+
throw new Error(`Nested field “${referenceType.name}.${field.name}” is a lookup, which an inline reference cannot serve: a nested document is read back with its referent, so nothing resolves a lookup inside one. Nest the referent through an inline reference instead.`);
|
|
254
|
+
}
|
|
249
255
|
}
|
|
250
256
|
}
|
|
251
257
|
}
|
|
@@ -289,9 +295,21 @@ export function labelFieldOf(searchType) {
|
|
|
289
295
|
: undefined;
|
|
290
296
|
}
|
|
291
297
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
298
|
+
* The Root Type a reference resolves labels from, by name: a `lookup`’s
|
|
299
|
+
* `target`, an `idOnly`’s {@link ReferenceField.labelSource}, or `undefined`
|
|
300
|
+
* when it resolves none. One reading for the two declarations, so a consumer
|
|
301
|
+
* never branches on the strategy to find the collection.
|
|
302
|
+
*/
|
|
303
|
+
export function labelSourceNameOf(field) {
|
|
304
|
+
return field.ref?.strategy === 'lookup'
|
|
305
|
+
? field.ref.target
|
|
306
|
+
: field.labelSource;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Every name a reference resolves labels from – a `lookup`’s `target` or an
|
|
310
|
+
* `idOnly`’s {@link ReferenceField.labelSource} – must be a declared type that
|
|
311
|
+
* can actually serve labels ({@link labelFieldOf}). Checked schema-wide,
|
|
312
|
+
* because a single declaration cannot see its siblings.
|
|
295
313
|
*
|
|
296
314
|
* That leaves only a {@link RootType}, without naming one: a label field is
|
|
297
315
|
* `searchable`, and {@link assertServiceableNestedFields} already rejects a
|
|
@@ -304,18 +322,29 @@ function assertResolvableLabelSources(types) {
|
|
|
304
322
|
for (const field of searchType.fields) {
|
|
305
323
|
const labelSource = field
|
|
306
324
|
.labelSource;
|
|
307
|
-
if (labelSource
|
|
308
|
-
|
|
325
|
+
if (labelSource !== undefined) {
|
|
326
|
+
if (field.kind !== 'reference') {
|
|
327
|
+
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.`);
|
|
328
|
+
}
|
|
329
|
+
// A lookup reads its labels from the `target` it already names, and an
|
|
330
|
+
// inline reference carries the referent’s own fields – so a second name
|
|
331
|
+
// for the same thing could only disagree with the first.
|
|
332
|
+
if (field.ref !== undefined && field.ref.strategy !== 'idOnly') {
|
|
333
|
+
throw new Error(`Reference “${searchType.name}.${field.name}” declares a label source on a ${field.ref.strategy} reference; only an idOnly reference does, for its facet buckets.`);
|
|
334
|
+
}
|
|
309
335
|
}
|
|
310
|
-
|
|
311
|
-
|
|
336
|
+
const sourceName = field.kind === 'reference' && field.ref?.strategy === 'lookup'
|
|
337
|
+
? field.ref.target
|
|
338
|
+
: labelSource;
|
|
339
|
+
if (sourceName === undefined) {
|
|
340
|
+
continue;
|
|
312
341
|
}
|
|
313
|
-
const source = byName.get(
|
|
342
|
+
const source = byName.get(sourceName);
|
|
314
343
|
if (source === undefined) {
|
|
315
|
-
throw new Error(`Reference “${searchType.name}.${field.name}” names unknown label source “${
|
|
344
|
+
throw new Error(`Reference “${searchType.name}.${field.name}” names unknown label source “${sourceName}”; declare a SearchType with that name.`);
|
|
316
345
|
}
|
|
317
346
|
if (labelFieldOf(source) === undefined) {
|
|
318
|
-
throw new Error(`Reference “${searchType.name}.${field.name}” uses label source “${
|
|
347
|
+
throw new Error(`Reference “${searchType.name}.${field.name}” uses label source “${sourceName}”, which must declare an output, searchable text field “${labelFieldNameOf(source)}”.`);
|
|
319
348
|
}
|
|
320
349
|
}
|
|
321
350
|
}
|
|
@@ -461,20 +490,23 @@ export function validateSearchType(searchType) {
|
|
|
461
490
|
if (field.output === true && field.ref === undefined) {
|
|
462
491
|
issue('missing-ref');
|
|
463
492
|
}
|
|
464
|
-
// `typeName`
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
//
|
|
468
|
-
//
|
|
493
|
+
// `typeName` names the emitted type, and only an `inline` reference
|
|
494
|
+
// must declare one: a `lookup` derives its name from the `target` it
|
|
495
|
+
// already names, and an `idOnly` surfaces as a bare IRI, emitting no
|
|
496
|
+
// type at all. Without a name where one is needed, an API surface has
|
|
497
|
+
// nothing to emit – the GraphQL one silently prints `undefined` as the
|
|
498
|
+
// field's type, publishing a contract that is not a schema.
|
|
469
499
|
if (field.output === true &&
|
|
470
|
-
field.ref
|
|
471
|
-
|
|
472
|
-
|
|
500
|
+
((field.ref?.strategy === 'inline' &&
|
|
501
|
+
field.ref.typeName === undefined) ||
|
|
502
|
+
(field.ref?.strategy === 'lookup' && field.ref.target === undefined))) {
|
|
473
503
|
issue('missing-ref-type-name');
|
|
474
504
|
}
|
|
475
|
-
// A join addresses the referent
|
|
476
|
-
//
|
|
477
|
-
|
|
505
|
+
// A join addresses the referent's collection – the one a lookup's
|
|
506
|
+
// `target` or an idOnly's `labelSource` names. With neither, the flag
|
|
507
|
+
// states an edge to nowhere.
|
|
508
|
+
if (field.joinable === true &&
|
|
509
|
+
labelSourceNameOf(field) === undefined) {
|
|
478
510
|
issue('joinable-without-label-source');
|
|
479
511
|
}
|
|
480
512
|
// An inline reference is stored as a NESTED OBJECT, not as an id an
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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",
|