@lde/search-typesense 0.26.0 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,12 +39,14 @@ export interface BuildSearchParamsOptions {
39
39
  */
40
40
  readonly maxFacetValues?: number;
41
41
  /**
42
- * Called for each `where` clause that compiles to nothing and is therefore
43
- * skipped: an unknown field, an operator that does not match the field’s
44
- * kind ({@link filterOperatorFor}), an empty `in` list, or a `range` with no
45
- * usable bound. Skipping keeps a malformed clause from reaching the engine
46
- * as garbage; supply this to log it instead of losing it silently. Through
47
- * the engine, a structurally invalid query throws up front
42
+ * Called for each `where` clause that does not compile as the caller wrote
43
+ * it – either because it states no constraint (an empty `in` list, a `range`
44
+ * with no usable bound, no criteria at all) and is skipped, or because every
45
+ * criterion is malformed (an unknown field, an operator that does not match
46
+ * the field’s kind – {@link filterOperatorFor}) or unsatisfiable, leaving a
47
+ * clause no document can match. Supply this to log the clause instead of
48
+ * losing it silently; the compiled query itself is faithful either way.
49
+ * Through the engine, a structurally invalid query throws up front
48
50
  * (`assertValidQuery`), so there only the clauses that state no constraint
49
51
  * reach this.
50
52
  */
@@ -1 +1 @@
1
- {"version":3,"file":"query-compiler.d.ts","sourceRoot":"","sources":["../src/query-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEzE,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,QAAQ,EAEb,KAAK,WAAW,EAChB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAarB;mDACmD;AACnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,SAAS,MAAM,EAAE,KACpB,UAAU,GAAG,SAAS,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;;;;OASG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,wBAA6B,GACrC,YAAY,CAAC,MAAM,CAAC,CA8BtB;AAuTD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD"}
1
+ {"version":3,"file":"query-compiler.d.ts","sourceRoot":"","sources":["../src/query-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEzE,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,QAAQ,EAEb,KAAK,WAAW,EAChB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAarB;mDACmD;AACnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,SAAS,MAAM,EAAE,KACpB,UAAU,GAAG,SAAS,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,wBAA6B,GACrC,YAAY,CAAC,MAAM,CAAC,CA8BtB;AA+UD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD"}
@@ -100,13 +100,15 @@ function queryFields(searchType, locale) {
100
100
  }
101
101
  return { names, weights };
102
102
  }
103
- /** AND-join the compiled `where` clauses; a clause that compiles to nothing is
104
- * skipped and reported to `onIgnoredFilter`. */
103
+ /** AND-join the compiled `where` clauses. A clause that states no constraint is
104
+ * skipped; one that can match nothing compiles to {@link MATCHES_NOTHING}
105
+ * rather than being skipped ({@link compileFilter}). Either way it did not
106
+ * compile as written, so it is reported to `onIgnoredFilter`. */
105
107
  function compileFilterBy(where, searchType, options) {
106
108
  return where
107
109
  .map((filter) => {
108
110
  const clause = compileFilter(filter, searchType, options);
109
- if (clause === undefined) {
111
+ if (clause === undefined || clause === MATCHES_NOTHING) {
110
112
  options.onIgnoredFilter?.(filter);
111
113
  }
112
114
  return clause;
@@ -134,15 +136,16 @@ function compileFilterBy(where, searchType, options) {
134
136
  * unknown field, an operator that mismatches the field’s kind) is *false*, and
135
137
  * `false || X` is X, so it drops out and its siblings still stand.
136
138
  *
137
- * Both readings hold **within** a clause. Neither survives a clause left with
138
- * no terms: it is skipped and reported to `onIgnoredFilter`, and a clause
139
- * missing from the conjunction constrains nothing – so a *false* criterion
140
- * alone in its clause still WIDENS the query. `filter_by` has no term meaning
141
- * “matches nothing” to compile it to instead. The engine never meets this
142
- * (`assertValidQuery` rejects every malformed criterion, and `isUnsatisfiable`
143
- * short-circuits the empty `id` membership before a query is dispatched); a
144
- * direct caller should read `onIgnoredFilter` firing as “this query no longer
145
- * asks what you asked”.
139
+ * Which reading applies decides what a clause left with **no terms** compiles
140
+ * to, and the two are opposites: a vacuous clause states no constraint, so it
141
+ * is skipped; an all-false clause states the query can have no answer, so it
142
+ * compiles to {@link MATCHES_NOTHING}. Skipping that one instead would drop a
143
+ * conjunct and widen the query – `false` compiled as `true`. Both are reported
144
+ * to `onIgnoredFilter`, because neither compiles to what the caller wrote.
145
+ *
146
+ * The engine never meets either: `assertValidQuery` rejects every malformed
147
+ * criterion, and `isUnsatisfiable` short-circuits the empty `id` membership
148
+ * before a query is dispatched.
146
149
  */
147
150
  function compileFilter(filter, searchType, options) {
148
151
  const terms = [];
@@ -156,10 +159,30 @@ function compileFilter(filter, searchType, options) {
156
159
  }
157
160
  }
158
161
  if (terms.length === 0) {
159
- return undefined;
162
+ // Every criterion was unusable, so the clause is FALSE – and `false && X`
163
+ // is false, however many clauses stand beside it. Leaving it out would drop
164
+ // a conjunct, and a conjunct missing from `filter_by` constrains nothing:
165
+ // the query would come back WIDER than the caller wrote, which is how a
166
+ // misspelled field name used to return the whole collection.
167
+ //
168
+ // A clause carrying no criteria at all says nothing rather than saying
169
+ // false, so it stays the vacuous no-op it reads as.
170
+ return filter.or.length === 0 ? undefined : MATCHES_NOTHING;
160
171
  }
161
172
  return terms.length === 1 ? terms[0] : `(${terms.join(' || ')})`;
162
173
  }
174
+ /**
175
+ * The term for a clause that can match no document: an **empty identity
176
+ * membership** – “the document is one of no documents”. Not a sentinel value
177
+ * but the literal reading, and the same one {@link isUnsatisfiable} gives an
178
+ * empty `id` membership in the IR; Typesense answers it with zero hits.
179
+ *
180
+ * A filter language has no keyword for `false`, so this stands in for one. It
181
+ * must be a term the engine *applies* rather than ignores – an empty string
182
+ * (``id:=` ` ``) is rejected outright as a filter value, and an omitted clause
183
+ * is read as true.
184
+ */
185
+ const MATCHES_NOTHING = `${ID_FIELD}:=[]`;
163
186
  /** A criterion that states **no constraint** – true for every document. */
164
187
  const VACUOUS = Symbol('vacuous');
165
188
  /** A criterion that matches nothing, or cannot be compiled at all – false. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search-typesense",
3
- "version": "0.26.0",
3
+ "version": "0.27.1",
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",
@@ -31,10 +31,10 @@
31
31
  "typesense": "^3.0.6"
32
32
  },
33
33
  "devDependencies": {
34
- "testcontainers": "^12.0.4"
34
+ "testcontainers": "^12.1.0"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@lde/dataset": "^0.7.9",
38
- "@lde/pipeline": "^0.36.0"
38
+ "@lde/pipeline": "^0.36.1"
39
39
  }
40
40
  }