@lde/search-typesense 0.48.0 → 0.50.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.
@@ -209,12 +209,17 @@ function typesenseFields(searchType, field, defaultLocale, defaultSortingField,
209
209
  // a membership filter on it exact (`compileMembership`).
210
210
  facet: names.facet === field.name,
211
211
  // Typesense answers a binned facet only on a field indexed for
212
- // sorting, so a field declaring bins is, sortable or not.
213
- sort: field.sortable === true ||
214
- (field.facetable === true && isBinnedRangeFacet(field)),
212
+ // sorting, so a field declaring bins is, sortable or not. A lookup
213
+ // reading its literals as periods sorts on their bounds instead.
214
+ sort: names.period === undefined &&
215
+ (field.sortable === true ||
216
+ (field.facetable === true && isBinnedRangeFacet(field))),
215
217
  // A `required` field is non-optional; so is the `default_sorting_field`,
216
- // which Typesense requires to be present. Everything else may be absent.
217
- optional: field.required !== true && field.name !== defaultSortingField,
218
+ // which Typesense requires to be present. Everything else may be absent
219
+ // – a required lookup reading its literals as periods too, since its
220
+ // value may be a period alone, stored in a companion.
221
+ optional: (field.required !== true || names.period !== undefined) &&
222
+ field.name !== defaultSortingField,
218
223
  ...referenceDeclaration(searchType, field, schema, collectionNameFor),
219
224
  },
220
225
  ];
@@ -246,10 +251,15 @@ function typesenseFields(searchType, field, defaultLocale, defaultSortingField,
246
251
  if (names.precision !== undefined) {
247
252
  fields.push(precisionField(names.precision));
248
253
  }
254
+ // A lookup reading its literals as periods stores them as a `period` does.
255
+ if (names.period !== undefined) {
256
+ fields.push(...periodFields(field, names));
257
+ }
249
258
  return fields;
250
259
  }
251
260
  /**
252
- * A `period` field: its values verbatim, stored for output but never indexed –
261
+ * A `period` field – or the periods a lookup reads its literals as: its values
262
+ * verbatim, stored for output but never indexed –
253
263
  * nothing filters or sorts on the text – and the two int64 bound companions
254
264
  * ({@link physicalFields}) that filters and sorts read instead. The bounds are
255
265
  * indexed for sorting whatever the field declares: a deployment may sort on a
@@ -257,14 +267,16 @@ function typesenseFields(searchType, field, defaultLocale, defaultSortingField,
257
267
  * field costs little next to a collection rebuilt the day it becomes so.
258
268
  */
259
269
  function periodFields(field, names) {
260
- // Every period has bounds; `physicalFields` names them for no other kind.
261
- const { start, end } = names.period;
270
+ // Every period has bounds; `physicalFields` names them for no other field.
271
+ const { values, start, end } = names.period;
262
272
  return [
263
273
  {
264
- name: field.name,
274
+ name: values,
265
275
  type: typesenseValueType(field),
266
276
  index: false,
267
- optional: field.required !== true,
277
+ // A lookup’s periods are optional however `required` it is: its value
278
+ // may be a referent instead.
279
+ optional: field.required !== true || field.kind !== 'period',
268
280
  },
269
281
  ...[start, end].map((name) => ({
270
282
  name,
package/dist/lookup.js CHANGED
@@ -161,10 +161,13 @@ function includeFields(target, wanted, below) {
161
161
  }
162
162
  else {
163
163
  physical.add(field.name);
164
- // A date is served at the precision its source gave, kept beside it.
165
- const precision = physicalFields(field).precision;
166
- if (precision !== undefined) {
167
- physical.add(precision);
164
+ // A date is served at the precision its source gave, kept beside it,
165
+ // and a lookup with the periods it reads its literals as.
166
+ const { precision, period } = physicalFields(field);
167
+ for (const companion of [precision, period?.values]) {
168
+ if (companion !== undefined) {
169
+ physical.add(companion);
170
+ }
168
171
  }
169
172
  }
170
173
  }
@@ -334,7 +334,7 @@ function compileLeaf(criterion, searchType, schema, prefix = '') {
334
334
  }
335
335
  // A criterion whose operator does not match the field's kind (e.g. `range` on
336
336
  // a keyword) would reach the engine as garbage syntax – skip it instead.
337
- if (filterOperatorFor(field.kind) !== filterOperator(criterion)) {
337
+ if (filterOperatorFor(field) !== filterOperator(criterion)) {
338
338
  return UNUSABLE;
339
339
  }
340
340
  if ('in' in criterion) {
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
package/dist/search.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { NESTED_DOCUMENT_TYPE, } from '@lde/search';
2
- import { assertTypeInSchema, assertValidQuery, displayLangOf, facetableFields, facetSearches, fieldNamed, filterOn, inheritedFacetPolicies, isDatePrecision, isInlineReference, isBinnedRangeFacet, isUnsatisfiable, joinGraph, labelFieldOf, labelSourceNamesOf, labelTargetNamesOf, localLookupTargetsOf, nestedReferenceType, outputFields, physicalFields, referencedTargetsOf, referenceFields, storedFacetRanges, storedTargetOf, } from '@lde/search/adapter';
2
+ import { assertTypeInSchema, assertValidQuery, displayLangOf, facetableFields, facetSearches, fieldNamed, filterOn, inheritedFacetPolicies, isDatePrecision, isInlineReference, isBinnedRangeFacet, isUnsatisfiable, joinGraph, labelFieldOf, labelSourceNamesOf, labelTargetNamesOf, literalKindOf, localLookupTargetsOf, nestedReferenceType, outputFields, physicalFields, referencedTargetsOf, referenceFields, storedFacetRanges, storedTargetOf, } from '@lde/search/adapter';
3
3
  import { buildSearchParams, escapeFilterValue, } from './query-compiler.js';
4
4
  import { deriveCollectionName } from './collection-name.js';
5
5
  import { resolveProjection, } from './lookup.js';
@@ -798,13 +798,36 @@ export function parseSearchResponse(response, searchType, labels, schema, refere
798
798
  function reconstructDocument(flat, searchType, labels, schema, referents) {
799
799
  const document = {};
800
800
  for (const field of outputFields(searchType)) {
801
- const value = logicalValue(flat, field, labels, schema, referents);
801
+ const value = withPeriods(flat, field, logicalValue(flat, field, labels, schema, referents));
802
802
  if (value !== undefined) {
803
803
  document[field.name] = value;
804
804
  }
805
805
  }
806
806
  return document;
807
807
  }
808
+ /**
809
+ * A lookup reading its literals as periods, with those periods added to its
810
+ * referents: one list in which each entry is a period, as stated, or a
811
+ * referent. A single-valued field stores only one of the two
812
+ * (`applyReferentsOrPeriods`). Any other field’s value is returned as it is.
813
+ */
814
+ function withPeriods(flat, field, referents) {
815
+ const periods = literalKindOf(field) === 'period'
816
+ ? physicalFields(field).period
817
+ : undefined;
818
+ if (periods === undefined) {
819
+ return referents;
820
+ }
821
+ const stored = flat[periods.values];
822
+ const values = (Array.isArray(stored) ? stored : [stored]).filter((value) => typeof value === 'string');
823
+ if (field.array !== true || values.length === 0) {
824
+ return values[0] ?? referents;
825
+ }
826
+ return [
827
+ ...values,
828
+ ...(referents ?? []),
829
+ ];
830
+ }
808
831
  function logicalValue(flat, field, labels, schema, referents) {
809
832
  switch (field.kind) {
810
833
  case 'text':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search-typesense",
3
- "version": "0.48.0",
3
+ "version": "0.50.0",
4
4
  "description": "Typesense implementation of the @lde/search SearchEngine port: collection-schema builder, query compiler, label-resolving result reconstruction, and blue/green index lifecycle. Engine-specific (Typesense) but domain-agnostic.",
5
5
  "repository": {
6
6
  "url": "git+https://github.com/ldelements/lde.git",
@@ -25,7 +25,7 @@
25
25
  "!**/*.tsbuildinfo"
26
26
  ],
27
27
  "dependencies": {
28
- "@lde/search": "^0.39.1",
28
+ "@lde/search": "^0.41.0",
29
29
  "@lde/text-normalization": "^0.3.0",
30
30
  "tslib": "^2.3.0",
31
31
  "typesense": "^3.2.0"