@lde/search-typesense 0.20.0 → 0.21.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.
- package/dist/query-compiler.d.ts.map +1 -1
- package/dist/query-compiler.js +68 -18
- package/package.json +2 -2
|
@@ -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,
|
|
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,EAEX,KAAK,WAAW,EAChB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAarB;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,wBAA6B,GACrC,YAAY,CAAC,MAAM,CAAC,CAkCtB;AA4PD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|
package/dist/query-compiler.js
CHANGED
|
@@ -109,35 +109,85 @@ function compileFilterBy(where, searchType, onIgnoredFilter) {
|
|
|
109
109
|
.filter((clause) => clause !== undefined)
|
|
110
110
|
.join(' && ');
|
|
111
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* One `where` clause: each of its criteria compiled and OR-joined with `||`,
|
|
114
|
+
* parenthesised so the disjunction binds tighter than the `&&` between clauses.
|
|
115
|
+
* A one-criterion clause – the ordinary single-field filter – compiles to a bare
|
|
116
|
+
* term: the parentheses would be harmless but would make every ordinary
|
|
117
|
+
* `filter_by` noisier to read and to assert on.
|
|
118
|
+
*
|
|
119
|
+
* A criterion that yields no term is dropped or voids the clause depending on
|
|
120
|
+
* **why**, because the two readings differ under `||`:
|
|
121
|
+
*
|
|
122
|
+
* - a **vacuous** criterion states no constraint (an empty `in` on a value
|
|
123
|
+
* field – a facet UI with nothing selected – or a `range` with no usable
|
|
124
|
+
* bound). It is therefore *true*, and `true || X` is true, so the whole clause
|
|
125
|
+
* constrains nothing and is skipped. Dropping only the criterion would
|
|
126
|
+
* NARROW the result to its siblings, the opposite of what an unset filter
|
|
127
|
+
* means;
|
|
128
|
+
* - an **unsatisfiable** or malformed criterion (an empty `id` membership, an
|
|
129
|
+
* unknown field, an operator that mismatches the field’s kind) is *false*, and
|
|
130
|
+
* `false || X` is X, so it drops out and its siblings still stand.
|
|
131
|
+
*
|
|
132
|
+
* A clause left with no terms either way is skipped and reported to
|
|
133
|
+
* `onIgnoredFilter`.
|
|
134
|
+
*/
|
|
112
135
|
function compileFilter(filter, searchType) {
|
|
136
|
+
const terms = [];
|
|
137
|
+
for (const criterion of filter.or) {
|
|
138
|
+
const outcome = compileCriterion(criterion, searchType);
|
|
139
|
+
if (outcome === VACUOUS) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
if (outcome !== UNUSABLE) {
|
|
143
|
+
terms.push(outcome);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (terms.length === 0) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
return terms.length === 1 ? terms[0] : `(${terms.join(' || ')})`;
|
|
150
|
+
}
|
|
151
|
+
/** A criterion that states **no constraint** – true for every document. */
|
|
152
|
+
const VACUOUS = Symbol('vacuous');
|
|
153
|
+
/** A criterion that matches nothing, or cannot be compiled at all – false. */
|
|
154
|
+
const UNUSABLE = Symbol('unusable');
|
|
155
|
+
/**
|
|
156
|
+
* What one criterion contributes: a Typesense term, or which of the two ways it
|
|
157
|
+
* yields none. The distinction is load-bearing under `||` ({@link compileFilter})
|
|
158
|
+
* and cannot be recovered from a bare `undefined`, so it is returned rather than
|
|
159
|
+
* re-derived by a second pass over the same rules.
|
|
160
|
+
*/
|
|
161
|
+
function compileCriterion(criterion, searchType) {
|
|
113
162
|
// `id` is the Typesense document key, not a declared field. Exact `:=`
|
|
114
163
|
// membership, like a non-facet field ({@link compileMembership}), so an IRI
|
|
115
164
|
// cannot partial-match on a shared path segment. (`fetchLabels` resolves
|
|
116
165
|
// labels with the looser `id:[…]`; these are deliberately not the same
|
|
117
|
-
// clause.)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
166
|
+
// clause.) An empty identity membership enumerates NO document, so it is
|
|
167
|
+
// unusable rather than vacuous – the one place the two readings diverge.
|
|
168
|
+
if (criterion.field === ID_FIELD) {
|
|
169
|
+
return 'in' in criterion && criterion.in.length > 0
|
|
170
|
+
? `${ID_FIELD}:=[${criterion.in.map(escapeFilterValue).join(',')}]`
|
|
171
|
+
: UNUSABLE;
|
|
122
172
|
}
|
|
123
|
-
const field = fieldNamed(searchType,
|
|
173
|
+
const field = fieldNamed(searchType, criterion.field);
|
|
124
174
|
if (field === undefined) {
|
|
125
|
-
return
|
|
175
|
+
return UNUSABLE;
|
|
126
176
|
}
|
|
127
|
-
// A
|
|
128
|
-
// keyword) would reach the engine as garbage syntax – skip it instead.
|
|
129
|
-
if (filterOperatorFor(field.kind) !== filterOperator(
|
|
130
|
-
return
|
|
177
|
+
// A criterion whose operator does not match the field's kind (e.g. `range` on
|
|
178
|
+
// a keyword) would reach the engine as garbage syntax – skip it instead.
|
|
179
|
+
if (filterOperatorFor(field.kind) !== filterOperator(criterion)) {
|
|
180
|
+
return UNUSABLE;
|
|
131
181
|
}
|
|
132
|
-
if ('in' in
|
|
133
|
-
return
|
|
134
|
-
? compileMembership(field,
|
|
135
|
-
:
|
|
182
|
+
if ('in' in criterion) {
|
|
183
|
+
return criterion.in.length > 0
|
|
184
|
+
? compileMembership(field, criterion.in)
|
|
185
|
+
: VACUOUS;
|
|
136
186
|
}
|
|
137
|
-
if ('range' in
|
|
138
|
-
return compileRange(field,
|
|
187
|
+
if ('range' in criterion) {
|
|
188
|
+
return compileRange(field, criterion.range) ?? VACUOUS;
|
|
139
189
|
}
|
|
140
|
-
return `${field.name}:=${
|
|
190
|
+
return `${field.name}:=${criterion.is}`;
|
|
141
191
|
}
|
|
142
192
|
/**
|
|
143
193
|
* A membership clause. A non-facet (tokenized) field uses the exact `:=`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search-typesense",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"!**/*.tsbuildinfo"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@lde/search": "^0.
|
|
28
|
+
"@lde/search": "^0.18.1",
|
|
29
29
|
"@lde/text-normalization": "^0.1.2",
|
|
30
30
|
"tslib": "^2.3.0",
|
|
31
31
|
"typesense": "^3.0.6"
|