@lde/search 0.30.0 → 0.31.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 CHANGED
@@ -6,12 +6,12 @@
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, 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';
9
+ export { assertTypeInSchema, physicalFields, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, localLookupTargetsOf, referencedTargetsOf, storedTargetOf, TARGET_FIELD, identityFieldOf, identityFieldName, inheritedFacetPolicies, 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';
13
13
  export { joinGraph, MAX_JOIN_DEPTH } from './join-graph.js';
14
14
  export type { JoinGraph } from './join-graph.js';
15
- export { filterOperatorFor, filterOperator, filterOn, isWelded, isUnsatisfiable, validateQuery, assertValidQuery, resolvePath, pageForOffset, } from './query.js';
16
- export type { FilterOperator, QueryIssue, ResolvedPath, PathFailure, } from './query.js';
15
+ export { filterOperatorFor, filterOperator, filterOn, isWelded, isUnsatisfiable, facetSearches, validateQuery, assertValidQuery, resolvePath, pageForOffset, } from './query.js';
16
+ export type { FacetSearch, FilterOperator, QueryIssue, ResolvedPath, PathFailure, } from './query.js';
17
17
  //# sourceMappingURL=adapter.d.ts.map
@@ -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,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"}
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,sBAAsB,EACtB,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,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,WAAW,EACX,cAAc,EACd,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,MAAM,YAAY,CAAC"}
package/dist/adapter.js CHANGED
@@ -6,8 +6,8 @@
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, 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';
9
+ export { assertTypeInSchema, physicalFields, irAlias, displayFieldName, displayFieldPattern, displayLangOf, searchableFields, facetableFields, filterableFields, sortableFields, outputFields, isInternalField, isInlineReference, nestedReferenceType, nestedFieldName, localLookupTargetsOf, referencedTargetsOf, storedTargetOf, TARGET_FIELD, identityFieldOf, identityFieldName, inheritedFacetPolicies, 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';
13
- export { filterOperatorFor, filterOperator, filterOn, isWelded, isUnsatisfiable, validateQuery, assertValidQuery, resolvePath, pageForOffset, } from './query.js';
13
+ export { filterOperatorFor, filterOperator, filterOn, isWelded, isUnsatisfiable, facetSearches, validateQuery, assertValidQuery, resolvePath, pageForOffset, } from './query.js';
package/dist/engine.d.ts CHANGED
@@ -24,6 +24,13 @@ import type { RootType, RootTypeOf, SearchSchema, SearchType } from './schema.js
24
24
  * Validation is not the caller’s job: it must hold for every surface and for
25
25
  * injected deployment policy.
26
26
  *
27
+ * Facets mean the same on every engine: a facet counts the documents under
28
+ * every `where` clause except its own (skip-own-filter), and reports every
29
+ * value its own clause selects, at count 0 when nothing else leaves it a
30
+ * document ({@link SearchQuery.facets}). An adapter plans the searches with
31
+ * `facetSearches` from `@lde/search/adapter`, so ownership cannot drift
32
+ * between engines.
33
+ *
27
34
  * `Types` is the literal tuple {@link searchSchema} captured: with a
28
35
  * `defineSearchType` declaration, `search()` only accepts the deployment’s
29
36
  * own types (a foreign type is a compile error) and returns facet/document
@@ -34,14 +41,22 @@ export interface SearchEngine<Types extends readonly SearchType[] = readonly Sea
34
41
  /** The declaration this engine serves – exposed so a surface can route and
35
42
  * a caller can enumerate the searchable types. */
36
43
  readonly schema: SearchSchema<Types>;
44
+ /**
45
+ * Answer one query: its hits, total and facets. The call fails as a whole:
46
+ * when any search an engine needs for it fails – including one that counts
47
+ * a single facet – the returned promise rejects and no hits come back. A
48
+ * caller that must keep its hits when facets fail asks for no facets here
49
+ * and gets them from {@link searchFacets}, which reports failures per query.
50
+ */
37
51
  search<T extends RootTypeOf<Types>>(searchType: T, query: SearchQuery): Promise<SearchResult<FacetFieldsOf<T>, OutputFieldsOf<T>>>;
38
52
  /**
39
53
  * The batch entry point: answer several facet-only queries against one type
40
54
  * in a single engine round-trip (where the engine supports one, e.g.
41
55
  * Typesense `multi_search`), returning one {@link FacetsOutcome} per query,
42
- * positionally aligned with `queries`. This is what keeps a faceted
43
- * listing – one skip-own-filter query variant per filtered facet – from
44
- * fanning out into per-facet engine calls. Only facets come back: an
56
+ * positionally aligned with `queries`. This is what keeps a sidebar that
57
+ * needs facets over more than one query – over the listing’s filters and
58
+ * over the whole collection – from fanning out into several engine calls.
59
+ * Only facets come back: an
45
60
  * engine answers every query in the batch facet-only (as if `limit: 0` and
46
61
  * without ordering, whatever the query carries), so hits are never
47
62
  * transferred. Reference-facet buckets carry resolved labels exactly as in
@@ -59,7 +74,8 @@ export interface SearchEngine<Types extends readonly SearchType[] = readonly Sea
59
74
  * facets, or the error that failed it. The `allSettled`-style shape
60
75
  * keeps one failed query (e.g. one failed `multi_search` entry) from
61
76
  * discarding its siblings’ results: a caller degrades exactly the facets of
62
- * the failed query. Discriminate with `'error' in outcome`.
77
+ * the failed query – all of them, also when the engine needed several
78
+ * searches to answer it. Discriminate with `'error' in outcome`.
63
79
  */
64
80
  export type FacetsOutcome<FacetField extends string = string> = {
65
81
  readonly facets: FacetMap<FacetField>;
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
package/dist/query.d.ts CHANGED
@@ -16,7 +16,14 @@ export interface SearchQuery {
16
16
  /** Numbered pagination. */
17
17
  readonly limit: number;
18
18
  readonly offset: number;
19
- /** Logical field names to return facet buckets for. */
19
+ /**
20
+ * Logical field names to return facets for. Each facet counts the documents
21
+ * under every `where` clause except its **own** (skip-own-filter), so a
22
+ * facet the user selected in still offers its other values; and it reports
23
+ * every value its own clause selects, with count 0 when the other clauses
24
+ * leave that value no documents, so a selection can always be undone. See
25
+ * {@link facetSearches} for what a clause owns.
26
+ */
20
27
  readonly facets: readonly string[];
21
28
  /** Selects the per-locale fields to query/sort on (from `Accept-Language`). */
22
29
  readonly locale: string;
@@ -178,6 +185,47 @@ export declare function filterOn(criterion: Criterion): Filter;
178
185
  * than dispatching it.
179
186
  */
180
187
  export declare function isUnsatisfiable(query: SearchQuery): boolean;
188
+ /**
189
+ * One search a query’s facets need, with the values each of its facets must
190
+ * report whatever their count. See {@link facetSearches}.
191
+ */
192
+ export interface FacetSearch {
193
+ /** The query to facet on: `where` without its facets’ own clauses, and
194
+ * `facets` the fields this search counts. */
195
+ readonly query: SearchQuery;
196
+ /** Per facet, the values its own clause selects. Only an own-filtered
197
+ * facet has an entry, and only an `in` or `is` selection names values. */
198
+ readonly selected: Readonly<Record<string, readonly string[]>>;
199
+ /** The facet whose own clauses this search removed; absent on the search
200
+ * that every facet without a clause of its own shares. */
201
+ readonly own?: string;
202
+ }
203
+ /**
204
+ * The fewest searches that compute a query’s {@link SearchQuery.facets} the way
205
+ * the port defines them: each facet counts the documents under every `where`
206
+ * clause except its own. Every facet whose field carries no clause of its own
207
+ * shares the query’s `where` – one search for all of them, so an unfiltered
208
+ * browse is a single search – and each own-filtered facet gets a search with
209
+ * its own clauses removed, together with the values those clauses selected.
210
+ * The searches keep the query’s text, paging and order; an adapter answering
211
+ * facets alone strips the hits itself. An adapter answers each facet from the
212
+ * search that counts it, so every engine computes facets alike.
213
+ *
214
+ * A clause is a facet’s **own** when every criterion in it names that field,
215
+ * on the searched type: an ordinary filter, and equally a same-field
216
+ * disjunction (`or: [{ created: { max: … } }, { created: { min: … } }]`).
217
+ * Those are a selection on one axis, and keeping them would offer back only
218
+ * what the user already picked. A clause spanning several fields is nobody’s
219
+ * own: it constrains the document as a whole, never one axis, so it always
220
+ * stays, and every facet counts the same documents under it. A criterion
221
+ * reached through a join path is a different axis than the local field of the
222
+ * same name ([ADR 18](../../docs/decisions/0018-filter-across-several-fields-with-one-clause.md)).
223
+ *
224
+ * A default a surface adds to `where` (a valid-only `status`) is a clause
225
+ * like any other: its facet counts across every value and reports the
226
+ * default’s values as selected.
227
+ */
228
+ export declare function facetSearches(query: SearchQuery): FacetSearch[];
181
229
  /**
182
230
  * One structural problem {@link validateQuery} found: the query references a
183
231
  * field the search type does not declare, uses it in a role it does not opt
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
package/dist/query.js CHANGED
@@ -54,6 +54,62 @@ export function isUnsatisfiable(query) {
54
54
  criterion.in.length === 0);
55
55
  });
56
56
  }
57
+ /**
58
+ * The fewest searches that compute a query’s {@link SearchQuery.facets} the way
59
+ * the port defines them: each facet counts the documents under every `where`
60
+ * clause except its own. Every facet whose field carries no clause of its own
61
+ * shares the query’s `where` – one search for all of them, so an unfiltered
62
+ * browse is a single search – and each own-filtered facet gets a search with
63
+ * its own clauses removed, together with the values those clauses selected.
64
+ * The searches keep the query’s text, paging and order; an adapter answering
65
+ * facets alone strips the hits itself. An adapter answers each facet from the
66
+ * search that counts it, so every engine computes facets alike.
67
+ *
68
+ * A clause is a facet’s **own** when every criterion in it names that field,
69
+ * on the searched type: an ordinary filter, and equally a same-field
70
+ * disjunction (`or: [{ created: { max: … } }, { created: { min: … } }]`).
71
+ * Those are a selection on one axis, and keeping them would offer back only
72
+ * what the user already picked. A clause spanning several fields is nobody’s
73
+ * own: it constrains the document as a whole, never one axis, so it always
74
+ * stays, and every facet counts the same documents under it. A criterion
75
+ * reached through a join path is a different axis than the local field of the
76
+ * same name ([ADR 18](../../docs/decisions/0018-filter-across-several-fields-with-one-clause.md)).
77
+ *
78
+ * A default a surface adds to `where` (a valid-only `status`) is a clause
79
+ * like any other: its facet counts across every value and reports the
80
+ * default’s values as selected.
81
+ */
82
+ export function facetSearches(query) {
83
+ const owners = query.where.map(ownedField);
84
+ const owned = new Set(owners.filter((field) => field !== undefined));
85
+ const facets = [...new Set(query.facets)];
86
+ const shared = facets.filter((field) => !owned.has(field));
87
+ const searches = shared.length > 0
88
+ ? [{ query: { ...query, facets: shared }, selected: {} }]
89
+ : [];
90
+ for (const field of facets.filter((field) => owned.has(field))) {
91
+ const own = query.where.filter((_, index) => owners[index] === field);
92
+ const selected = [
93
+ ...new Set(own
94
+ .flatMap((filter) => filter.or)
95
+ .flatMap((criterion) => 'in' in criterion
96
+ ? criterion.in
97
+ : 'is' in criterion
98
+ ? [String(criterion.is)]
99
+ : [])),
100
+ ];
101
+ searches.push({
102
+ query: {
103
+ ...query,
104
+ where: query.where.filter((_, index) => owners[index] !== field),
105
+ facets: [field],
106
+ },
107
+ selected: selected.length > 0 ? { [field]: selected } : {},
108
+ own: field,
109
+ });
110
+ }
111
+ return searches;
112
+ }
57
113
  /**
58
114
  * The checks every leaf criterion answers to, wherever it sits: the field is
59
115
  * declared and `filterable` on the type it constrains, its operator matches
@@ -368,3 +424,26 @@ export function assertValidQuery(query, searchType, schema, joins) {
368
424
  export function pageForOffset(offset, limit) {
369
425
  return limit > 0 ? Math.floor(offset / limit) + 1 : 1;
370
426
  }
427
+ /**
428
+ * The field a clause belongs to, or `undefined` when it belongs to none: every
429
+ * criterion must constrain the same axis ({@link axisOf}).
430
+ */
431
+ function ownedField(filter) {
432
+ const [first, ...rest] = filter.or;
433
+ if (first === undefined) {
434
+ return undefined;
435
+ }
436
+ const key = axisOf(first);
437
+ return rest.every((criterion) => axisOf(criterion) === key) ? key : undefined;
438
+ }
439
+ /**
440
+ * The axis one criterion constrains: its field, qualified by the join path it
441
+ * reaches that field through. `dataset.publisher` and a local `publisher` are
442
+ * different questions, so a selection on one is no selection on the other.
443
+ * Facets are not offered on joined fields, so a joined clause never matches a
444
+ * facet and simply never drops.
445
+ */
446
+ function axisOf(criterion) {
447
+ const on = criterion.on ?? [];
448
+ return on.length === 0 ? criterion.field : [...on, criterion.field].join('.');
449
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAuBhD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,YAAY,GACzB,IAAI,CAiUN"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAuBhD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,YAAY,GACzB,IAAI,CA8WN"}
package/dist/testing.js CHANGED
@@ -179,6 +179,45 @@ export function describeSearchEngineContract(name, engine) {
179
179
  }
180
180
  }
181
181
  });
182
+ it('counts a facet without its own clause, and reports what that clause selects', async () => {
183
+ // Selecting a value no document carries leaves no hits. The facet still
184
+ // offers every other value, counted as if nothing were selected, and
185
+ // keeps the selected one at count 0 so it can be undone.
186
+ const absent = 'urn:lde:testing:absent-facet-value';
187
+ for (const searchType of types()) {
188
+ const field = facetableFields(searchType).find((candidate) => candidate.filterable === true &&
189
+ filterOperatorFor(candidate.kind) === 'in');
190
+ if (field === undefined) {
191
+ continue;
192
+ }
193
+ const facetQuery = {
194
+ ...browse(searchType),
195
+ facets: [field.name],
196
+ };
197
+ const selected = {
198
+ ...facetQuery,
199
+ where: [filterOn({ field: field.name, in: [absent] })],
200
+ };
201
+ const [unfiltered, filtered] = await engine().searchFacets(searchType, [
202
+ facetQuery,
203
+ selected,
204
+ ]);
205
+ if ('error' in unfiltered || 'error' in filtered) {
206
+ throw new Error('Expected facets outcomes.');
207
+ }
208
+ const others = unfiltered.facets[field.name]?.buckets ?? [];
209
+ const result = await engine().search(searchType, selected);
210
+ expect(result.total).toBe(0);
211
+ for (const buckets of [
212
+ filtered.facets[field.name]?.buckets ?? [],
213
+ result.facets[field.name]?.buckets ?? [],
214
+ ]) {
215
+ expect(buckets).toHaveLength(others.length + 1);
216
+ expect(buckets).toEqual(expect.arrayContaining([...others]));
217
+ expect(buckets).toContainEqual(expect.objectContaining({ value: absent, count: 0 }));
218
+ }
219
+ }
220
+ });
182
221
  it('resolves an empty searchFacets batch to an empty list', async () => {
183
222
  for (const searchType of types()) {
184
223
  await expect(engine().searchFacets(searchType, [])).resolves.toEqual([]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search",
3
- "version": "0.30.0",
3
+ "version": "0.31.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",