@lde/search 0.28.1 → 0.30.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/engine.d.ts +46 -5
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +3 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/join-graph.js +4 -2
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +139 -42
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +33 -16
- package/dist/schema.d.ts +111 -46
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +239 -83
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +44 -8
- package/package.json +2 -2
package/dist/adapter.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* deployment author declaring a schema and running searches needs none of
|
|
7
7
|
* this; the main entry point carries that (small) authoring surface.
|
|
8
8
|
*/
|
|
9
|
-
export { assertTypeInSchema, physicalFields, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName,
|
|
9
|
+
export { assertTypeInSchema, physicalFields, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, localLookupTargetsOf, referencedTargetsOf, storedTargetOf, TARGET_FIELD, identityFieldOf, identityFieldName, referenceFields, referenceTypeNamed, rootTypeNamed, inlineFramingDepth, fieldNamed, datasetField, ID_FIELD, AND_KEY, OR_KEY, labelFieldOf, labelFieldNameOf, labelSourceNamesOf, labelTargetNamesOf, documentKeyOf, DEFAULT_LABEL_FIELD, isRangeFacet, isBinnedRangeFacet, 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 { matchLanguage } from './language.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,
|
|
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,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,MAAM,EACN,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,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,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,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,QAAQ,EACR,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,cAAc,EACd,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,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,
|
|
9
|
+
export { assertTypeInSchema, physicalFields, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, localLookupTargetsOf, referencedTargetsOf, storedTargetOf, TARGET_FIELD, identityFieldOf, identityFieldName, referenceFields, referenceTypeNamed, rootTypeNamed, inlineFramingDepth, fieldNamed, datasetField, ID_FIELD, AND_KEY, OR_KEY, labelFieldOf, labelFieldNameOf, labelSourceNamesOf, labelTargetNamesOf, documentKeyOf, DEFAULT_LABEL_FIELD, isRangeFacet, isBinnedRangeFacet, isAbsoluteIri, isoToUnixSeconds, unixSecondsToIso, } from './schema.js';
|
|
10
10
|
export { physicalNameTokens } from './physical-name.js';
|
|
11
11
|
export { matchLanguage } from './language.js';
|
|
12
12
|
export { joinGraph, MAX_JOIN_DEPTH } from './join-graph.js';
|
package/dist/engine.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface SearchEngine<Types extends readonly SearchType[] = readonly Sea
|
|
|
41
41
|
* Typesense `multi_search`), returning one {@link FacetsOutcome} per query,
|
|
42
42
|
* positionally aligned with `queries`. This is what keeps a faceted
|
|
43
43
|
* listing – one skip-own-filter query variant per filtered facet – from
|
|
44
|
-
* fanning out into per-facet engine calls. Only
|
|
44
|
+
* fanning out into per-facet engine calls. Only facets come back: an
|
|
45
45
|
* engine answers every query in the batch facet-only (as if `limit: 0` and
|
|
46
46
|
* without ordering, whatever the query carries), so hits are never
|
|
47
47
|
* transferred. Reference-facet buckets carry resolved labels exactly as in
|
|
@@ -56,7 +56,7 @@ export interface SearchEngine<Types extends readonly SearchType[] = readonly Sea
|
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
58
|
* One query’s outcome in a {@link SearchEngine.searchFacets} batch – its
|
|
59
|
-
*
|
|
59
|
+
* facets, or the error that failed it. The `allSettled`-style shape
|
|
60
60
|
* keeps one failed query (e.g. one failed `multi_search` entry) from
|
|
61
61
|
* discarding its siblings’ results: a caller degrades exactly the facets of
|
|
62
62
|
* the failed query. Discriminate with `'error' in outcome`.
|
|
@@ -73,10 +73,36 @@ export interface SearchResult<FacetField extends string = string, OutputField ex
|
|
|
73
73
|
readonly facets: FacetMap<FacetField>;
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
76
|
+
* Facets keyed by facet field name. `Partial` because a result carries a facet
|
|
77
|
+
* only for the fields the query asked for, not every facetable field.
|
|
78
78
|
*/
|
|
79
|
-
export type FacetMap<FacetField extends string = string> = Readonly<Partial<Record<FacetField,
|
|
79
|
+
export type FacetMap<FacetField extends string = string> = Readonly<Partial<Record<FacetField, Facet>>>;
|
|
80
|
+
/**
|
|
81
|
+
* One facet over the documents a query matches: its buckets and, for a
|
|
82
|
+
* numeric (`integer`/`number`/`date`) field, the extent of the values those
|
|
83
|
+
* documents hold – the bounds a range slider needs.
|
|
84
|
+
*
|
|
85
|
+
* A facet buckets per distinct value, or – a numeric field declaring
|
|
86
|
+
* `facetRanges` – into those bins. Only a numeric facet has an extent.
|
|
87
|
+
*/
|
|
88
|
+
export interface Facet {
|
|
89
|
+
readonly buckets: readonly FacetBucket[];
|
|
90
|
+
/**
|
|
91
|
+
* The lowest and highest value among the matching documents, for a numeric
|
|
92
|
+
* facet. Absent on any other facet, and when no matching document holds a
|
|
93
|
+
* value.
|
|
94
|
+
*/
|
|
95
|
+
readonly extent?: FacetExtent;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The lowest and highest value a range facet’s documents hold, stored terms
|
|
99
|
+
* like every other value at this seam: a `date` in Unix seconds, which a
|
|
100
|
+
* surface serves as ISO 8601 the way it serves the field itself.
|
|
101
|
+
*/
|
|
102
|
+
export interface FacetExtent {
|
|
103
|
+
readonly min: number;
|
|
104
|
+
readonly max: number;
|
|
105
|
+
}
|
|
80
106
|
/**
|
|
81
107
|
* The facet-field-name union of a search type – the keys a {@link SearchResult}’s
|
|
82
108
|
* `facets` can hold. Narrow for a declaration captured as a literal (via
|
|
@@ -133,7 +159,22 @@ export type SearchValue = string | number | boolean | readonly string[] | Locali
|
|
|
133
159
|
*/
|
|
134
160
|
export interface NestedDocument {
|
|
135
161
|
readonly [field: string]: SearchValue | undefined;
|
|
162
|
+
/**
|
|
163
|
+
* The `name` of the Root Type this document is read through, where the
|
|
164
|
+
* field that carries it is a `lookup` naming **several** targets – the
|
|
165
|
+
* declaration it was resolved or stored against. Such a lookup serves
|
|
166
|
+
* referents of any of its targets under one field, and this is what tells a
|
|
167
|
+
* surface which one each referent is, so it can type the value per referent
|
|
168
|
+
* instead of claiming one kind for all of them. Absent where there is
|
|
169
|
+
* nothing to tell apart: a single-target lookup, or an inline reference’s
|
|
170
|
+
* entry. Keyed by a symbol so it can never collide with a declared field
|
|
171
|
+
* name.
|
|
172
|
+
*/
|
|
173
|
+
readonly [NESTED_DOCUMENT_TYPE]?: string;
|
|
136
174
|
}
|
|
175
|
+
/** The key under which a {@link NestedDocument} carries the `name` of the
|
|
176
|
+
* Root Type it is read through. */
|
|
177
|
+
export declare const NESTED_DOCUMENT_TYPE: unique symbol;
|
|
137
178
|
/**
|
|
138
179
|
* A JSON-LD-style language map (`@container: @language`, `@set` arrays); the key
|
|
139
180
|
* `und` carries untagged (`@none`) values. The surface flattens it to a
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,WAAW,YAAY,CAC3B,KAAK,SAAS,SAAS,UAAU,EAAE,GAAG,SAAS,UAAU,EAAE;IAE3D;uDACmD;IACnD,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,CAAC,CAAC,SAAS,UAAU,CAAC,KAAK,CAAC,EAChC,UAAU,EAAE,CAAC,EACb,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,CAAC,CAAC,SAAS,UAAU,CAAC,KAAK,CAAC,EACtC,UAAU,EAAE,CAAC,EACb,OAAO,EAAE,SAAS,WAAW,EAAE,GAC9B,OAAO,CAAC,SAAS,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;CACxD;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,CAAC,UAAU,SAAS,MAAM,GAAG,MAAM,IAC1D;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1E,+EAA+E;AAC/E,MAAM,WAAW,YAAY,CAC3B,UAAU,SAAS,MAAM,GAAG,MAAM,EAClC,WAAW,SAAS,MAAM,GAAG,MAAM;IAEnC,QAAQ,CAAC,IAAI,EAAE,SAAS,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;CACvC;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,UAAU,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CACjE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,WAAW,EAAE,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ 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
8
|
export type { SearchQuery, ReferenceProjection, Filter, Criterion, CriterionBase, WeldedCriterion, Sort, } from './query.js';
|
|
9
|
-
export
|
|
9
|
+
export { NESTED_DOCUMENT_TYPE } from './engine.js';
|
|
10
|
+
export type { SearchEngine, SearchResult, SearchHit, ResultDocument, SearchValue, LocalizedValue, Reference, NestedDocument, Facet, FacetBucket, FacetExtent, FacetMap, FacetsOutcome, FacetFieldsOf, OutputFieldsOf, } from './engine.js';
|
|
10
11
|
export type { FramedNode } from './frame-by-type.js';
|
|
11
12
|
//# 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,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,SAAS,EACT,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,eAAe,EACf,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,SAAS,EACT,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,eAAe,EACf,IAAI,GACL,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,cAAc,EACd,KAAK,EACL,WAAW,EACX,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
|
@@ -27,3 +27,6 @@ export { joinGraph, MAX_JOIN_DEPTH } from './join-graph.js';
|
|
|
27
27
|
// policy (`queryDefaults`) appends to `where`, so it belongs on the authoring
|
|
28
28
|
// surface and not only on the adapter one.
|
|
29
29
|
export { filterOn } from './query.js';
|
|
30
|
+
// Engine port + the logical result document returned across it. An engine is
|
|
31
|
+
// bound to the whole SearchSchema at construction by its adapter factory.
|
|
32
|
+
export { NESTED_DOCUMENT_TYPE } from './engine.js';
|
package/dist/join-graph.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { labelSourceNamesOf, 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
|
|
@@ -103,7 +103,9 @@ function declaredEdges(searchType, byName) {
|
|
|
103
103
|
// field, which a Reference Type cannot carry (a nested field is `output`
|
|
104
104
|
// only). So a joinable edge always has a collection at the far end without
|
|
105
105
|
// a rule of its own.
|
|
106
|
-
|
|
106
|
+
// One target, never several: `validateSearchType` refuses `joinable` on a
|
|
107
|
+
// reference naming more than one.
|
|
108
|
+
const target = byName.get(labelSourceNamesOf(field)[0]);
|
|
107
109
|
const claimed = claimedBy.get(target.name);
|
|
108
110
|
if (claimed !== undefined) {
|
|
109
111
|
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.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;AAGzC,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;AAGzC,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAoBL,KAAK,eAAe,EAIpB,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;AAmLD;;;;;;;;;;;;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,7 +1,7 @@
|
|
|
1
1
|
import { fold } from '@lde/text-normalization';
|
|
2
2
|
import { matchLanguage } from './language.js';
|
|
3
3
|
import { buildSubjectIndex, frameSubjects, } from './frame-by-type.js';
|
|
4
|
-
import { assertTypeInSchema, displayFieldName, documentKeyOf, fieldNamed,
|
|
4
|
+
import { assertTypeInSchema, displayFieldName, documentKeyOf, fieldNamed, identityFieldOf, inheritedFacetPolicies, inlineFramingDepth, irAlias, isAbsoluteIri, isInternalField, isInlineReference, isoToUnixSeconds, localLookupTargetsOf, physicalFields, referencedTargetsOf, referenceTypeNamed, storedTargetOf, TARGET_FIELD, } from './schema.js';
|
|
5
5
|
/** The context a projection runs with when its caller supplied none. */
|
|
6
6
|
const NO_PROJECTION_CONTEXT = {};
|
|
7
7
|
/**
|
|
@@ -65,19 +65,21 @@ function pruneInternalFields(document, searchType, schema) {
|
|
|
65
65
|
if (schema === undefined || field.kind !== 'reference') {
|
|
66
66
|
continue;
|
|
67
67
|
}
|
|
68
|
-
const
|
|
69
|
-
? referenceTypeNamed(schema, field.ref.typeName)
|
|
70
|
-
:
|
|
68
|
+
const nestedTypes = field.ref?.strategy === 'inline'
|
|
69
|
+
? [referenceTypeNamed(schema, field.ref.typeName)].filter((type) => type !== undefined)
|
|
70
|
+
: localLookupTargetsOf(field, schema);
|
|
71
71
|
const nested = document[field.name];
|
|
72
|
-
if (
|
|
72
|
+
if (nestedTypes.length === 0 || nested === undefined) {
|
|
73
73
|
continue;
|
|
74
74
|
}
|
|
75
75
|
for (const referent of Array.isArray(nested) ? nested : [nested]) {
|
|
76
|
-
pruneInternalFields(referent,
|
|
76
|
+
pruneInternalFields(referent, nestedTypes.length === 1
|
|
77
|
+
? nestedTypes[0]
|
|
78
|
+
: storedTargetOf(referent, nestedTypes), schema);
|
|
77
79
|
}
|
|
78
80
|
// Two entries that differed only in their node IRI are one entry now that
|
|
79
81
|
// it is gone – two edge nodes stating the same fact.
|
|
80
|
-
if (
|
|
82
|
+
if (nestedTypes[0].class === undefined && Array.isArray(nested)) {
|
|
81
83
|
document[field.name] = dedupeBy(nested, (referent) => JSON.stringify(referent));
|
|
82
84
|
}
|
|
83
85
|
}
|
|
@@ -254,9 +256,9 @@ function applyField(document, node, field, searchType, schema, context, nested)
|
|
|
254
256
|
// is an entry like any other, minus its `id`. Same reason as above for
|
|
255
257
|
// needing a schema: without one there is no target to project through.
|
|
256
258
|
if (field.kind === 'reference' && schema !== undefined) {
|
|
257
|
-
const
|
|
258
|
-
if (
|
|
259
|
-
const endpoints = applyNestedReferents(document, valuesOf(node, alias), field,
|
|
259
|
+
const localTargets = localLookupTargetsOf(field, schema);
|
|
260
|
+
if (localTargets.length > 0) {
|
|
261
|
+
const endpoints = applyNestedReferents(document, valuesOf(node, alias), field, localTargets, schema, context);
|
|
260
262
|
// A `local` lookup stores an object, so its own id is a level deeper than
|
|
261
263
|
// a filter can reach – an engine welds conditions on an entry's LEAF
|
|
262
264
|
// fields only. `filterable` therefore fans out the id beside the object,
|
|
@@ -270,8 +272,12 @@ function applyField(document, node, field, searchType, schema, context, nested)
|
|
|
270
272
|
return applyText(document, langValuesOf(node, alias), field);
|
|
271
273
|
case 'keyword':
|
|
272
274
|
return applyFacet(document, literalsOf(node, alias), field, schema);
|
|
273
|
-
case 'reference':
|
|
274
|
-
|
|
275
|
+
case 'reference': {
|
|
276
|
+
const targetsById = new Map();
|
|
277
|
+
const values = referenceValues(node, alias, field, schema, targetsById);
|
|
278
|
+
rememberTargets(document, targetsById);
|
|
279
|
+
return applyFacet(document, values, field, schema, targetsById);
|
|
280
|
+
}
|
|
275
281
|
case 'integer':
|
|
276
282
|
return setNumber(document, field.name, toInteger(firstLiteralOf(node, alias)));
|
|
277
283
|
case 'number':
|
|
@@ -316,27 +322,87 @@ function applyField(document, node, field, searchType, schema, context, nested)
|
|
|
316
322
|
* referent’s node IRIs therefore sees a key instead, which is why the two are
|
|
317
323
|
* worth declaring together only deliberately.
|
|
318
324
|
*/
|
|
319
|
-
function referenceValues(node, alias, field, schema) {
|
|
320
|
-
const targetName = labelSourceNameOf(field);
|
|
321
|
-
if (schema === undefined || targetName === undefined) {
|
|
322
|
-
return irisOf(node, alias);
|
|
323
|
-
}
|
|
325
|
+
function referenceValues(node, alias, field, schema, targetsById) {
|
|
324
326
|
// Guaranteed declared: `searchSchema` validates that every named target
|
|
325
327
|
// resolves, and the projection only ever runs against a type of its schema.
|
|
326
|
-
const
|
|
327
|
-
if (
|
|
328
|
+
const targets = referencedTargetsOf(field, schema);
|
|
329
|
+
if (targets.length === 0) {
|
|
328
330
|
return irisOf(node, alias);
|
|
329
331
|
}
|
|
330
|
-
const keyFieldName = target.key.field;
|
|
331
332
|
return valuesOf(node, alias)
|
|
332
333
|
.map((value) => {
|
|
333
334
|
const iri = iriString(value);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
335
|
+
if (iri === undefined) {
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
// Each referent is re-keyed through the target IT belongs to – the one
|
|
339
|
+
// its `rdf:type` matches – so a person keyed on an authority IRI and an
|
|
340
|
+
// organization keyed on nothing store, under one field, the ids their
|
|
341
|
+
// own collections file them under.
|
|
342
|
+
const target = targetOfReferent(value, targets);
|
|
343
|
+
const keyed = target.key === undefined
|
|
344
|
+
? iri
|
|
345
|
+
: documentKeyOf(target, iri, isObject(value)
|
|
346
|
+
? keyCandidatesOf(value, target, target.key.field)
|
|
347
|
+
: []);
|
|
348
|
+
// Remembered only where there was a choice: a single-target reference
|
|
349
|
+
// stays byte-identical to what it was, symbol keys included. Keyed by
|
|
350
|
+
// what the field STORES – the value after its own `transform`, which
|
|
351
|
+
// `applyFacet` applies before it reads this map.
|
|
352
|
+
if (keyed !== undefined && targets.length > 1) {
|
|
353
|
+
targetsById.set(field.transform === undefined ? keyed : field.transform(keyed), target);
|
|
354
|
+
}
|
|
355
|
+
return keyed;
|
|
337
356
|
})
|
|
338
357
|
.filter((value) => value !== undefined);
|
|
339
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* The Root Type a framed referent belongs to, among the targets its reference
|
|
361
|
+
* names: the first whose `class` is among the referent’s `rdf:type`s, which
|
|
362
|
+
* the extraction emits and framing carries as `@type`; else the first target
|
|
363
|
+
* declared. One target needs no evidence at all. This is the one reading of
|
|
364
|
+
* *which of several targets is this*, so re-keying, the facet policy and the
|
|
365
|
+
* stored discriminator ({@link TARGET_FIELD}) cannot disagree.
|
|
366
|
+
*/
|
|
367
|
+
function targetOfReferent(value, targets) {
|
|
368
|
+
if (targets.length === 1 || !isObject(value)) {
|
|
369
|
+
return targets[0];
|
|
370
|
+
}
|
|
371
|
+
const raw = value['@type'];
|
|
372
|
+
const classes = new Set((Array.isArray(raw) ? raw : [raw]).filter((type) => typeof type === 'string'));
|
|
373
|
+
return targets.find((target) => classes.has(target.class)) ?? targets[0];
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* The targets the reference values of a projected node were resolved through,
|
|
377
|
+
* by id – kept beside the node under a symbol, so it never reaches the
|
|
378
|
+
* writer, for the one reader that needs it after projection: the identity
|
|
379
|
+
* companion of an inline reference, which admits an entry’s id to a facet by
|
|
380
|
+
* the policy of the target that id belongs to. Only a reference naming
|
|
381
|
+
* several targets has anything to remember.
|
|
382
|
+
*/
|
|
383
|
+
const REFERENT_TARGETS = Symbol('referentTargets');
|
|
384
|
+
function rememberTargets(document, targetsById) {
|
|
385
|
+
if (targetsById.size === 0) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const node = document;
|
|
389
|
+
const remembered = (node[REFERENT_TARGETS] ??= new Map());
|
|
390
|
+
for (const [id, target] of targetsById) {
|
|
391
|
+
remembered.set(id, target);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Whether an id deserves a facet bucket, by the {@link FacetKeys facet policy}
|
|
396
|
+
* of the target it belongs to: admitted where that target declares none, and
|
|
397
|
+
* by its `only` where it does. `targetName` is the target the id was resolved
|
|
398
|
+
* or stored against; an id whose target is unknown falls back to the first
|
|
399
|
+
* declared, the same precedence every other reading of several targets uses.
|
|
400
|
+
*/
|
|
401
|
+
function admitsFacet(policies, targets, targetName, id) {
|
|
402
|
+
// Policies are inherited from targets, so where any exists a target does.
|
|
403
|
+
const policy = policies.get(targetName ?? targets[0].name);
|
|
404
|
+
return policy === undefined || policy.only(id);
|
|
405
|
+
}
|
|
340
406
|
/**
|
|
341
407
|
* Project a text field. **Display** (when `output`) preserves *every* language
|
|
342
408
|
* present – one label per language, or every label of it for an `array` field
|
|
@@ -450,7 +516,7 @@ function foldedSearchValue(values) {
|
|
|
450
516
|
* already-read raw values).
|
|
451
517
|
*
|
|
452
518
|
* A reference inheriting a {@link FacetKeys facet policy} from the type it
|
|
453
|
-
* names ({@link
|
|
519
|
+
* names ({@link inheritedFacetPolicies}) also writes the `${name}_facet` companion
|
|
454
520
|
* the engine facets instead of the field: the subset of **what the field
|
|
455
521
|
* stores** that the policy admits. So it is written after the field’s own
|
|
456
522
|
* `transform` and the IRI filter, from the same `values` – for a keyed target
|
|
@@ -469,7 +535,7 @@ function foldedSearchValue(values) {
|
|
|
469
535
|
* engine rejects, or – where it does not type-check, as inside a nested
|
|
470
536
|
* document – a value the API cannot serialize.
|
|
471
537
|
*/
|
|
472
|
-
function applyFacet(document, raw, field, schema) {
|
|
538
|
+
function applyFacet(document, raw, field, schema, targetsById) {
|
|
473
539
|
// A `reference` stores identity, so what it stores must be an IRI whatever
|
|
474
540
|
// route the value arrived by. {@link iriString} guards the graph path, but a
|
|
475
541
|
// `transform` runs after it and a `from` projection value never passes it at
|
|
@@ -481,21 +547,25 @@ function applyFacet(document, raw, field, schema) {
|
|
|
481
547
|
const folded = dedupe(values.map((value) => fold(value)));
|
|
482
548
|
const names = physicalFields(field, schema);
|
|
483
549
|
const searchField = names.search[0];
|
|
484
|
-
const
|
|
550
|
+
const policies = inheritedFacetPolicies(field, schema);
|
|
551
|
+
const inherited = policies.size > 0;
|
|
485
552
|
// `names.facet` is the companion exactly when a policy is inherited –
|
|
486
|
-
// physicalFields reads the same `
|
|
553
|
+
// physicalFields reads the same `inheritedFacetPolicies`.
|
|
487
554
|
const facetField = names.facet;
|
|
488
555
|
// The companion is a subset of what the field STORES – for a single-valued
|
|
489
556
|
// field its first value, not the first admitted of all of them, or the facet
|
|
490
557
|
// would count a value no filter on the field can reproduce.
|
|
491
558
|
const stored = field.array === true ? values : values.slice(0, 1);
|
|
492
|
-
const
|
|
559
|
+
const targets = referencedTargetsOf(field, schema);
|
|
560
|
+
const admitted = !inherited
|
|
561
|
+
? []
|
|
562
|
+
: stored.filter((id) => admitsFacet(policies, targets, targetsById?.get(id)?.name, id));
|
|
493
563
|
if (field.array === true) {
|
|
494
564
|
setArray(document, field.name, stored);
|
|
495
565
|
if (field.searchable) {
|
|
496
566
|
setArray(document, searchField, folded);
|
|
497
567
|
}
|
|
498
|
-
if (
|
|
568
|
+
if (inherited) {
|
|
499
569
|
setArray(document, facetField, admitted);
|
|
500
570
|
}
|
|
501
571
|
return;
|
|
@@ -504,7 +574,7 @@ function applyFacet(document, raw, field, schema) {
|
|
|
504
574
|
if (field.searchable) {
|
|
505
575
|
setString(document, searchField, folded[0]);
|
|
506
576
|
}
|
|
507
|
-
if (
|
|
577
|
+
if (inherited) {
|
|
508
578
|
setString(document, facetField, admitted[0]);
|
|
509
579
|
}
|
|
510
580
|
}
|
|
@@ -555,7 +625,7 @@ function applyInlineReference(document, node, alias, field, schema, context, nes
|
|
|
555
625
|
if (referenceType === undefined) {
|
|
556
626
|
return;
|
|
557
627
|
}
|
|
558
|
-
const referents = applyNestedReferents(document, valuesOf(node, alias), field, referenceType, schema, context);
|
|
628
|
+
const referents = applyNestedReferents(document, valuesOf(node, alias), field, [referenceType], schema, context);
|
|
559
629
|
applyIdentityCompanion(document, referents, field, schema, nested);
|
|
560
630
|
}
|
|
561
631
|
/**
|
|
@@ -584,11 +654,24 @@ function applyIdentityCompanion(document, referents, field, schema, nested) {
|
|
|
584
654
|
// keeps the first referent and drops the rest, and a companion holding an id
|
|
585
655
|
// from a dropped one would match a filter whose hit shows no such entry.
|
|
586
656
|
const stored = field.array === true ? referents : referents.slice(0, 1);
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
657
|
+
// Each id with the target it was resolved through, for the facet policy
|
|
658
|
+
// below: a stored endpoint says so itself ({@link TARGET_FIELD}), a bare id
|
|
659
|
+
// was remembered when the entry was projected ({@link rememberTargets}).
|
|
660
|
+
const targetById = new Map();
|
|
661
|
+
for (const referent of stored) {
|
|
662
|
+
const remembered = referent[REFERENT_TARGETS];
|
|
663
|
+
const raw = referent[identity];
|
|
664
|
+
for (const value of Array.isArray(raw) ? raw : [raw]) {
|
|
665
|
+
const id = identityValue(value);
|
|
666
|
+
if (id === undefined || targetById.has(id)) {
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
targetById.set(id, isObject(value) && typeof value[TARGET_FIELD] === 'string'
|
|
670
|
+
? value[TARGET_FIELD]
|
|
671
|
+
: remembered?.get(id)?.name);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
const ids = [...targetById.keys()];
|
|
592
675
|
if (ids.length === 0) {
|
|
593
676
|
return;
|
|
594
677
|
}
|
|
@@ -596,9 +679,10 @@ function applyIdentityCompanion(document, referents, field, schema, nested) {
|
|
|
596
679
|
// Same rule as every other facetable reference: where the target declares a
|
|
597
680
|
// facet policy, the facet reads a narrowed companion of its own, so an
|
|
598
681
|
// excluded id is never seen by the engine rather than merely unlabelled.
|
|
599
|
-
const
|
|
600
|
-
if (
|
|
601
|
-
|
|
682
|
+
const policies = inheritedFacetPolicies(field, schema);
|
|
683
|
+
if (policies.size > 0) {
|
|
684
|
+
const targets = referencedTargetsOf(identityFieldOf(field, schema), schema);
|
|
685
|
+
setIdentity(document, names.facet, ids.filter((id) => admitsFacet(policies, targets, targetById.get(id), id)), field, nested);
|
|
602
686
|
}
|
|
603
687
|
}
|
|
604
688
|
/**
|
|
@@ -676,14 +760,27 @@ function identityValue(value) {
|
|
|
676
760
|
* top-level document of that type would be – so the stored id is the one the
|
|
677
761
|
* target’s collection files it under, and the lookup can find it.
|
|
678
762
|
*/
|
|
679
|
-
function applyNestedReferents(document, values, field,
|
|
763
|
+
function applyNestedReferents(document, values, field, nestedTypes, schema, context) {
|
|
680
764
|
// One node may stand for several entries: a weldable leaf is single-valued,
|
|
681
765
|
// so an edge the graph gave several roles or several endpoints fans out into
|
|
682
766
|
// one entry per combination BEFORE it is projected (ADR 26).
|
|
683
767
|
const referents = values
|
|
684
768
|
.filter(isObject)
|
|
685
|
-
.flatMap((value) =>
|
|
686
|
-
|
|
769
|
+
.flatMap((value) => {
|
|
770
|
+
// A lookup naming several targets projects each referent through the
|
|
771
|
+
// one it belongs to, and stores which – so an engine adapter can read
|
|
772
|
+
// an entry no collection answers for through the right declaration.
|
|
773
|
+
const nestedType = nestedTypes.length === 1
|
|
774
|
+
? nestedTypes[0]
|
|
775
|
+
: targetOfReferent(value, nestedTypes);
|
|
776
|
+
return tuplesOf(value, nestedType, field).map((tuple) => {
|
|
777
|
+
const projected = projectFields(tuple, nestedType, schema, context, true);
|
|
778
|
+
if (nestedTypes.length > 1 && Object.keys(projected).length > 0) {
|
|
779
|
+
projected[TARGET_FIELD] = nestedType.name;
|
|
780
|
+
}
|
|
781
|
+
return projected;
|
|
782
|
+
});
|
|
783
|
+
})
|
|
687
784
|
// Fields, not identity, are what makes something a referent: a literal
|
|
688
785
|
// value object under the alias (dirty source data), or a node this
|
|
689
786
|
// reference type reads nothing from, projects nothing and is no referent.
|
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, isoToUnixSeconds, nestedReferenceType, } from './schema.js';
|
|
1
|
+
import { fieldNamed, filterOperatorFor, ID_FIELD, isoToUnixSeconds, nestedReferenceType, referencedTargetsOf, } from './schema.js';
|
|
2
2
|
import { MAX_JOIN_DEPTH } from './join-graph.js';
|
|
3
3
|
/** Whether a criterion welds conditions to one entry ({@link WeldedCriterion}). */
|
|
4
4
|
export function isWelded(criterion) {
|
|
@@ -261,7 +261,7 @@ export function validateQuery(query, searchType, schema, joins) {
|
|
|
261
261
|
issues.push({ part: 'facets', field: name, reason: 'not-facetable' });
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
|
-
collectProjectionIssues(query.resolve, searchType, schema, issues);
|
|
264
|
+
collectProjectionIssues(query.resolve, [searchType], schema, issues);
|
|
265
265
|
for (const sort of query.orderBy) {
|
|
266
266
|
if (sort.field !== 'relevance' &&
|
|
267
267
|
fieldNamed(searchType, sort.field) === undefined) {
|
|
@@ -289,35 +289,52 @@ export function validateQuery(query, searchType, schema, joins) {
|
|
|
289
289
|
* target that does not exist, so reporting it too would bury the one mistake
|
|
290
290
|
* under its consequences.
|
|
291
291
|
*/
|
|
292
|
-
function collectProjectionIssues(projection,
|
|
292
|
+
function collectProjectionIssues(projection, searchTypes, schema, issues) {
|
|
293
293
|
for (const [name, level] of Object.entries(projection ?? {})) {
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
// A level is read against every type it may be reached through: a lookup
|
|
295
|
+
// naming several targets resolves a referent of any of them, so the level
|
|
296
|
+
// below is valid wherever ANY of those targets serves it – a field one
|
|
297
|
+
// target declares and another does not is exactly what a polymorphic
|
|
298
|
+
// selection asks for.
|
|
299
|
+
const fields = searchTypes
|
|
300
|
+
.map((searchType) => fieldNamed(searchType, name))
|
|
301
|
+
.filter((field) => field !== undefined);
|
|
302
|
+
if (fields.length === 0) {
|
|
296
303
|
issues.push({ part: 'resolve', field: name, reason: 'unknown-field' });
|
|
297
304
|
continue;
|
|
298
305
|
}
|
|
299
306
|
// Descending into an inline reference is free, so a nested level is valid
|
|
300
307
|
// wherever the entries exist – what it is *for* is the lookup below it.
|
|
301
|
-
|
|
302
|
-
|
|
308
|
+
// `searchSchema` holds the targets of one lookup to one declaration per
|
|
309
|
+
// name, so a name is nested or a lookup, never both; each is still read
|
|
310
|
+
// on its own here rather than one deciding for the other.
|
|
311
|
+
const nested = [];
|
|
312
|
+
for (const field of fields) {
|
|
313
|
+
const referenceType = nestedReferenceType(schema, field);
|
|
314
|
+
if (referenceType !== undefined) {
|
|
315
|
+
nested.push(referenceType);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (nested.length > 0) {
|
|
303
319
|
collectProjectionIssues(level.resolve, nested, schema, issues);
|
|
304
|
-
continue;
|
|
305
320
|
}
|
|
306
|
-
|
|
307
|
-
|
|
321
|
+
const lookups = fields.filter((field) => field.kind === 'reference' && field.ref?.strategy === 'lookup');
|
|
322
|
+
if (lookups.length === 0) {
|
|
323
|
+
if (nested.length === 0) {
|
|
324
|
+
issues.push({ part: 'resolve', field: name, reason: 'not-resolvable' });
|
|
325
|
+
}
|
|
308
326
|
continue;
|
|
309
327
|
}
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
if (target === undefined) {
|
|
328
|
+
const targets = lookups.flatMap((field) => referencedTargetsOf(field, schema));
|
|
329
|
+
if (targets.length === 0) {
|
|
313
330
|
// searchSchema rejects a lookup whose target it cannot resolve, so this
|
|
314
331
|
// is a query built against a different schema than the engine serves.
|
|
315
332
|
issues.push({ part: 'resolve', field: name, reason: 'not-resolvable' });
|
|
316
333
|
continue;
|
|
317
334
|
}
|
|
318
335
|
for (const wanted of level.fields ?? []) {
|
|
319
|
-
const
|
|
320
|
-
if (
|
|
336
|
+
const served = targets.some((target) => fieldNamed(target, wanted)?.output === true);
|
|
337
|
+
if (!served) {
|
|
321
338
|
issues.push({
|
|
322
339
|
part: 'resolve',
|
|
323
340
|
field: `${name}.${wanted}`,
|
|
@@ -325,7 +342,7 @@ function collectProjectionIssues(projection, searchType, schema, issues) {
|
|
|
325
342
|
});
|
|
326
343
|
}
|
|
327
344
|
}
|
|
328
|
-
collectProjectionIssues(level.resolve,
|
|
345
|
+
collectProjectionIssues(level.resolve, targets, schema, issues);
|
|
329
346
|
}
|
|
330
347
|
}
|
|
331
348
|
/** Throw on the first structurally invalid query part ({@link validateQuery}),
|