@lde/search-typesense 0.25.1 → 0.27.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/query-compiler.d.ts
CHANGED
|
@@ -39,13 +39,16 @@ export interface BuildSearchParamsOptions {
|
|
|
39
39
|
*/
|
|
40
40
|
readonly maxFacetValues?: number;
|
|
41
41
|
/**
|
|
42
|
-
* Called for each `where` clause that
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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
|
|
50
|
+
* (`assertValidQuery`), so there only the clauses that state no constraint
|
|
51
|
+
* reach this.
|
|
49
52
|
*/
|
|
50
53
|
readonly onIgnoredFilter?: (filter: Filter) => void;
|
|
51
54
|
}
|
|
@@ -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
|
|
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"}
|
package/dist/query-compiler.js
CHANGED
|
@@ -100,13 +100,15 @@ function queryFields(searchType, locale) {
|
|
|
100
100
|
}
|
|
101
101
|
return { names, weights };
|
|
102
102
|
}
|
|
103
|
-
/** AND-join the compiled `where` clauses
|
|
104
|
-
* skipped
|
|
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,8 +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
|
-
*
|
|
138
|
-
*
|
|
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.
|
|
139
149
|
*/
|
|
140
150
|
function compileFilter(filter, searchType, options) {
|
|
141
151
|
const terms = [];
|
|
@@ -149,10 +159,30 @@ function compileFilter(filter, searchType, options) {
|
|
|
149
159
|
}
|
|
150
160
|
}
|
|
151
161
|
if (terms.length === 0) {
|
|
152
|
-
|
|
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;
|
|
153
171
|
}
|
|
154
172
|
return terms.length === 1 ? terms[0] : `(${terms.join(' || ')})`;
|
|
155
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}:=[]`;
|
|
156
186
|
/** A criterion that states **no constraint** – true for every document. */
|
|
157
187
|
const VACUOUS = Symbol('vacuous');
|
|
158
188
|
/** A criterion that matches nothing, or cannot be compiled at all – false. */
|
|
@@ -237,11 +267,21 @@ function compileMembership(field, values) {
|
|
|
237
267
|
? `${field.name}:=${list}`
|
|
238
268
|
: `${field.name}:${list}`;
|
|
239
269
|
}
|
|
240
|
-
/** An inclusive Typesense range clause, or `undefined` when neither bound is
|
|
270
|
+
/** An inclusive Typesense range clause, or `undefined` when neither bound is
|
|
271
|
+
* usable. Which of the two readings that is – the caller set no bounds, or the
|
|
272
|
+
* codec rejected the ones they set – is deliberately NOT decided here: a
|
|
273
|
+
* criterion dropped from a clause narrows the query while a clause dropped
|
|
274
|
+
* from the conjunction widens it, so the same reading cannot be right in both
|
|
275
|
+
* positions. `validateQuery` rejects an unreadable bound outright instead,
|
|
276
|
+
* which is the only answer that neither widens nor narrows. */
|
|
241
277
|
function compileRange(field, range) {
|
|
242
278
|
const name = field.name;
|
|
243
|
-
|
|
244
|
-
|
|
279
|
+
// A bound a caller sent as `null` – a GraphQL variable left unfilled, which
|
|
280
|
+
// the surface passes through – is a bound NOT SET, not a bound of `null`.
|
|
281
|
+
// Read literally it reaches the engine as `datePosted:[null..…]`, which
|
|
282
|
+
// Typesense rejects outright.
|
|
283
|
+
const min = storedBound(field, range.min ?? undefined);
|
|
284
|
+
const max = storedBound(field, range.max ?? undefined);
|
|
245
285
|
if (min !== undefined && max !== undefined) {
|
|
246
286
|
return `${name}:[${min}..${max}]`;
|
|
247
287
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search-typesense",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.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.
|
|
28
|
+
"@lde/search": "^0.23.0",
|
|
29
29
|
"@lde/text-normalization": "^0.1.2",
|
|
30
30
|
"tslib": "^2.3.0",
|
|
31
31
|
"typesense": "^3.0.6"
|