@mastra/libsql 0.0.4 → 0.0.5-alpha.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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +15 -0
- package/dist/_tsup-dts-rollup.d.cts +18 -29
- package/dist/_tsup-dts-rollup.d.ts +18 -29
- package/dist/index.cjs +285 -184
- package/dist/index.js +285 -184
- package/package.json +2 -2
- package/src/storage/index.ts +18 -10
- package/src/vector/index.test.ts +154 -119
- package/src/vector/index.ts +63 -27
- package/src/vector/sql-builder.ts +243 -175
|
@@ -1,58 +1,93 @@
|
|
|
1
1
|
import type { InValue } from '@libsql/client';
|
|
2
|
+
import { parseFieldKey } from '@mastra/core/utils';
|
|
2
3
|
import type {
|
|
3
|
-
VectorFilter,
|
|
4
4
|
BasicOperator,
|
|
5
5
|
NumericOperator,
|
|
6
6
|
ArrayOperator,
|
|
7
7
|
ElementOperator,
|
|
8
8
|
LogicalOperator,
|
|
9
|
-
|
|
9
|
+
VectorFilter,
|
|
10
10
|
} from '@mastra/core/vector/filter';
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
type OperatorType =
|
|
13
13
|
| BasicOperator
|
|
14
14
|
| NumericOperator
|
|
15
15
|
| ArrayOperator
|
|
16
16
|
| ElementOperator
|
|
17
17
|
| LogicalOperator
|
|
18
18
|
| '$contains'
|
|
19
|
-
|
|
|
19
|
+
| '$size';
|
|
20
20
|
|
|
21
21
|
type FilterOperator = {
|
|
22
22
|
sql: string;
|
|
23
23
|
needsValue: boolean;
|
|
24
|
-
transformValue?: (
|
|
24
|
+
transformValue?: () => any;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
type OperatorFn = (key: string, value?: any) => FilterOperator;
|
|
28
28
|
|
|
29
29
|
// Helper functions to create operators
|
|
30
30
|
const createBasicOperator = (symbol: string) => {
|
|
31
|
-
return (key: string): FilterOperator =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
return (key: string, value: any): FilterOperator => {
|
|
32
|
+
const jsonPathKey = parseJsonPathKey(key);
|
|
33
|
+
return {
|
|
34
|
+
sql: `CASE
|
|
35
|
+
WHEN ? IS NULL THEN json_extract(metadata, '$."${jsonPathKey}"') IS ${symbol === '=' ? '' : 'NOT'} NULL
|
|
36
|
+
ELSE json_extract(metadata, '$."${jsonPathKey}"') ${symbol} ?
|
|
37
|
+
END`,
|
|
38
|
+
needsValue: true,
|
|
39
|
+
transformValue: () => {
|
|
40
|
+
// Return the values directly, not in an object
|
|
41
|
+
return [value, value];
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
};
|
|
42
45
|
};
|
|
43
46
|
const createNumericOperator = (symbol: string) => {
|
|
44
|
-
return (key: string): FilterOperator =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
return (key: string): FilterOperator => {
|
|
48
|
+
const jsonPathKey = parseJsonPathKey(key);
|
|
49
|
+
return {
|
|
50
|
+
sql: `CAST(json_extract(metadata, '$."${jsonPathKey}"') AS NUMERIC) ${symbol} ?`,
|
|
51
|
+
needsValue: true,
|
|
52
|
+
};
|
|
53
|
+
};
|
|
48
54
|
};
|
|
49
55
|
|
|
50
56
|
const validateJsonArray = (key: string) =>
|
|
51
|
-
`json_valid(json_extract(metadata, '$."${
|
|
52
|
-
AND json_type(json_extract(metadata, '$."${
|
|
57
|
+
`json_valid(json_extract(metadata, '$."${key}"'))
|
|
58
|
+
AND json_type(json_extract(metadata, '$."${key}"')) = 'array'`;
|
|
59
|
+
|
|
60
|
+
const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
|
|
61
|
+
|
|
62
|
+
function buildElemMatchConditions(value: any) {
|
|
63
|
+
const conditions = Object.entries(value).map(([field, fieldValue]) => {
|
|
64
|
+
if (field.startsWith('$')) {
|
|
65
|
+
// Direct operators on array elements ($in, $gt, etc)
|
|
66
|
+
const { sql, values } = buildCondition('elem.value', { [field]: fieldValue }, '');
|
|
67
|
+
// Replace the metadata path with elem.value
|
|
68
|
+
const elemSql = sql.replace(pattern, 'elem.value');
|
|
69
|
+
return { sql: elemSql, values };
|
|
70
|
+
} else if (typeof fieldValue === 'object' && !Array.isArray(fieldValue)) {
|
|
71
|
+
// Nested field with operators (count: { $gt: 20 })
|
|
72
|
+
const { sql, values } = buildCondition(field, fieldValue, '');
|
|
73
|
+
// Replace the field path with elem.value path
|
|
74
|
+
const elemSql = sql.replace(pattern, `json_extract(elem.value, '$."${field}"')`);
|
|
75
|
+
return { sql: elemSql, values };
|
|
76
|
+
} else {
|
|
77
|
+
const parsedFieldKey = parseFieldKey(field);
|
|
78
|
+
// Simple field equality (warehouse: 'A')
|
|
79
|
+
return {
|
|
80
|
+
sql: `json_extract(elem.value, '$."${parsedFieldKey}"') = ?`,
|
|
81
|
+
values: [fieldValue],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return conditions;
|
|
87
|
+
}
|
|
53
88
|
|
|
54
89
|
// Define all filter operators
|
|
55
|
-
|
|
90
|
+
const FILTER_OPERATORS: Record<OperatorType, OperatorFn> = {
|
|
56
91
|
$eq: createBasicOperator('='),
|
|
57
92
|
$ne: createBasicOperator('!='),
|
|
58
93
|
$gt: createNumericOperator('>'),
|
|
@@ -61,101 +96,121 @@ export const FILTER_OPERATORS: Record<string, OperatorFn> = {
|
|
|
61
96
|
$lte: createNumericOperator('<='),
|
|
62
97
|
|
|
63
98
|
// Array Operators
|
|
64
|
-
$in: (key: string, value: any) =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
99
|
+
$in: (key: string, value: any) => {
|
|
100
|
+
const jsonPathKey = parseJsonPathKey(key);
|
|
101
|
+
const arr = Array.isArray(value) ? value : [value];
|
|
102
|
+
if (arr.length === 0) {
|
|
103
|
+
return { sql: '1 = 0', needsValue: true, transformValue: () => [] };
|
|
104
|
+
}
|
|
105
|
+
const paramPlaceholders = arr.map(() => '?').join(',');
|
|
106
|
+
return {
|
|
107
|
+
sql: `(
|
|
108
|
+
CASE
|
|
109
|
+
WHEN ${validateJsonArray(jsonPathKey)} THEN
|
|
110
|
+
EXISTS (
|
|
111
|
+
SELECT 1 FROM json_each(json_extract(metadata, '$."${jsonPathKey}"')) as elem
|
|
112
|
+
WHERE elem.value IN (SELECT value FROM json_each(?))
|
|
113
|
+
)
|
|
114
|
+
ELSE json_extract(metadata, '$."${jsonPathKey}"') IN (${paramPlaceholders})
|
|
115
|
+
END
|
|
116
|
+
)`,
|
|
117
|
+
needsValue: true,
|
|
118
|
+
transformValue: () => [JSON.stringify(arr), ...arr],
|
|
119
|
+
};
|
|
120
|
+
},
|
|
84
121
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
122
|
+
$nin: (key: string, value: any) => {
|
|
123
|
+
const jsonPathKey = parseJsonPathKey(key);
|
|
124
|
+
const arr = Array.isArray(value) ? value : [value];
|
|
125
|
+
if (arr.length === 0) {
|
|
126
|
+
return { sql: '1 = 1', needsValue: true, transformValue: () => [] };
|
|
127
|
+
}
|
|
128
|
+
const paramPlaceholders = arr.map(() => '?').join(',');
|
|
129
|
+
return {
|
|
130
|
+
sql: `(
|
|
131
|
+
CASE
|
|
132
|
+
WHEN ${validateJsonArray(jsonPathKey)} THEN
|
|
133
|
+
NOT EXISTS (
|
|
134
|
+
SELECT 1 FROM json_each(json_extract(metadata, '$."${jsonPathKey}"')) as elem
|
|
135
|
+
WHERE elem.value IN (SELECT value FROM json_each(?))
|
|
136
|
+
)
|
|
137
|
+
ELSE json_extract(metadata, '$."${jsonPathKey}"') NOT IN (${paramPlaceholders})
|
|
138
|
+
END
|
|
139
|
+
)`,
|
|
140
|
+
needsValue: true,
|
|
141
|
+
transformValue: () => [JSON.stringify(arr), ...arr],
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
$all: (key: string, value: any) => {
|
|
145
|
+
const jsonPathKey = parseJsonPathKey(key);
|
|
146
|
+
let sql: string;
|
|
147
|
+
const arrayValue = Array.isArray(value) ? value : [value];
|
|
148
|
+
|
|
149
|
+
if (arrayValue.length === 0) {
|
|
150
|
+
// If the array is empty, always return false (no matches)
|
|
151
|
+
sql = '1 = 0';
|
|
152
|
+
} else {
|
|
153
|
+
sql = `(
|
|
154
|
+
CASE
|
|
155
|
+
WHEN ${validateJsonArray(jsonPathKey)} THEN
|
|
156
|
+
NOT EXISTS (
|
|
157
|
+
SELECT value
|
|
158
|
+
FROM json_each(?)
|
|
159
|
+
WHERE value NOT IN (
|
|
160
|
+
SELECT value
|
|
161
|
+
FROM json_each(json_extract(metadata, '$."${jsonPathKey}"'))
|
|
96
162
|
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
throw new Error('$elemMatch requires an object with conditions');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// For nested object conditions
|
|
113
|
-
const conditions = Object.entries(value).map(([field, fieldValue]) => {
|
|
114
|
-
if (field.startsWith('$')) {
|
|
115
|
-
// Direct operators on array elements ($in, $gt, etc)
|
|
116
|
-
const { sql, values } = buildCondition('elem.value', { [field]: fieldValue }, '');
|
|
117
|
-
// Replace the metadata path with elem.value
|
|
118
|
-
const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
|
|
119
|
-
const elemSql = sql.replace(pattern, 'elem.value');
|
|
120
|
-
return { sql: elemSql, values };
|
|
121
|
-
} else if (typeof fieldValue === 'object' && !Array.isArray(fieldValue)) {
|
|
122
|
-
// Nested field with operators (count: { $gt: 20 })
|
|
123
|
-
const { sql, values } = buildCondition(field, fieldValue, '');
|
|
124
|
-
// Replace the field path with elem.value path
|
|
125
|
-
const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
|
|
126
|
-
const elemSql = sql.replace(pattern, `json_extract(elem.value, '$."${field}"')`);
|
|
127
|
-
return { sql: elemSql, values };
|
|
128
|
-
} else {
|
|
129
|
-
// Simple field equality (warehouse: 'A')
|
|
130
|
-
return {
|
|
131
|
-
sql: `json_extract(elem.value, '$."${field}"') = ?`,
|
|
132
|
-
values: [fieldValue],
|
|
133
|
-
};
|
|
163
|
+
)
|
|
164
|
+
ELSE FALSE
|
|
165
|
+
END
|
|
166
|
+
)`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
sql,
|
|
171
|
+
needsValue: true,
|
|
172
|
+
transformValue: () => {
|
|
173
|
+
if (arrayValue.length === 0) {
|
|
174
|
+
return [];
|
|
134
175
|
}
|
|
135
|
-
|
|
176
|
+
return [JSON.stringify(arrayValue)];
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
$elemMatch: (key: string, value: any) => {
|
|
181
|
+
const jsonPathKey = parseJsonPathKey(key);
|
|
182
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
183
|
+
throw new Error('$elemMatch requires an object with conditions');
|
|
184
|
+
}
|
|
136
185
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
186
|
+
// For nested object conditions
|
|
187
|
+
const conditions = buildElemMatchConditions(value);
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
sql: `(
|
|
191
|
+
CASE
|
|
192
|
+
WHEN ${validateJsonArray(jsonPathKey)} THEN
|
|
193
|
+
EXISTS (
|
|
194
|
+
SELECT 1
|
|
195
|
+
FROM json_each(json_extract(metadata, '$."${jsonPathKey}"')) as elem
|
|
196
|
+
WHERE ${conditions.map(c => c.sql).join(' AND ')}
|
|
197
|
+
)
|
|
198
|
+
ELSE FALSE
|
|
199
|
+
END
|
|
200
|
+
)`,
|
|
201
|
+
needsValue: true,
|
|
202
|
+
transformValue: () => conditions.flatMap(c => c.values),
|
|
203
|
+
};
|
|
204
|
+
},
|
|
153
205
|
|
|
154
206
|
// Element Operators
|
|
155
|
-
$exists: (key: string) =>
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
207
|
+
$exists: (key: string) => {
|
|
208
|
+
const jsonPathKey = parseJsonPathKey(key);
|
|
209
|
+
return {
|
|
210
|
+
sql: `json_extract(metadata, '$."${jsonPathKey}"') IS NOT NULL`,
|
|
211
|
+
needsValue: false,
|
|
212
|
+
};
|
|
213
|
+
},
|
|
159
214
|
|
|
160
215
|
// Logical Operators
|
|
161
216
|
$and: (key: string) => ({
|
|
@@ -171,27 +226,30 @@ export const FILTER_OPERATORS: Record<string, OperatorFn> = {
|
|
|
171
226
|
sql: `NOT (${key})`,
|
|
172
227
|
needsValue: false,
|
|
173
228
|
}),
|
|
174
|
-
$size: (key: string, paramIndex: number) =>
|
|
175
|
-
|
|
229
|
+
$size: (key: string, paramIndex: number) => {
|
|
230
|
+
const jsonPathKey = parseJsonPathKey(key);
|
|
231
|
+
return {
|
|
232
|
+
sql: `(
|
|
176
233
|
CASE
|
|
177
|
-
WHEN json_type(json_extract(metadata, '$."${
|
|
178
|
-
json_array_length(json_extract(metadata, '$."${
|
|
234
|
+
WHEN json_type(json_extract(metadata, '$."${jsonPathKey}"')) = 'array' THEN
|
|
235
|
+
json_array_length(json_extract(metadata, '$."${jsonPathKey}"')) = $${paramIndex}
|
|
179
236
|
ELSE FALSE
|
|
180
237
|
END
|
|
181
238
|
)`,
|
|
182
|
-
|
|
183
|
-
|
|
239
|
+
needsValue: true,
|
|
240
|
+
};
|
|
241
|
+
},
|
|
184
242
|
// /**
|
|
185
243
|
// * Regex Operators
|
|
186
244
|
// * Supports case insensitive and multiline
|
|
187
245
|
// */
|
|
188
246
|
// $regex: (key: string): FilterOperator => ({
|
|
189
|
-
// sql: `json_extract(metadata, '$."${
|
|
247
|
+
// sql: `json_extract(metadata, '$."${toJsonPathKey(key)}"') = ?`,
|
|
190
248
|
// needsValue: true,
|
|
191
249
|
// transformValue: (value: any) => {
|
|
192
250
|
// const pattern = typeof value === 'object' ? value.$regex : value;
|
|
193
251
|
// const options = typeof value === 'object' ? value.$options || '' : '';
|
|
194
|
-
// let sql = `json_extract(metadata, '$."${
|
|
252
|
+
// let sql = `json_extract(metadata, '$."${toJsonPathKey(key)}"')`;
|
|
195
253
|
|
|
196
254
|
// // Handle multiline
|
|
197
255
|
// // if (options.includes('m')) {
|
|
@@ -247,63 +305,73 @@ export const FILTER_OPERATORS: Record<string, OperatorFn> = {
|
|
|
247
305
|
// };
|
|
248
306
|
// },
|
|
249
307
|
// }),
|
|
250
|
-
$contains: (key: string) =>
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
function traverse(obj: any, path: string[] = []) {
|
|
275
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
276
|
-
const currentPath = [...path, k];
|
|
277
|
-
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
278
|
-
traverse(v, currentPath);
|
|
279
|
-
} else {
|
|
280
|
-
paths.push(currentPath.join('.'));
|
|
281
|
-
values.push(v);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
308
|
+
$contains: (key: string, value: any) => {
|
|
309
|
+
const jsonPathKey = parseJsonPathKey(key);
|
|
310
|
+
let sql;
|
|
311
|
+
if (Array.isArray(value)) {
|
|
312
|
+
sql = `(
|
|
313
|
+
SELECT ${validateJsonArray(jsonPathKey)}
|
|
314
|
+
AND EXISTS (
|
|
315
|
+
SELECT 1
|
|
316
|
+
FROM json_each(json_extract(metadata, '$."${jsonPathKey}"')) as m
|
|
317
|
+
WHERE m.value IN (SELECT value FROM json_each(?))
|
|
318
|
+
)
|
|
319
|
+
)`;
|
|
320
|
+
} else if (typeof value === 'string') {
|
|
321
|
+
sql = `lower(json_extract(metadata, '$."${jsonPathKey}"')) LIKE '%' || lower(?) || '%' ESCAPE '\\'`;
|
|
322
|
+
} else {
|
|
323
|
+
sql = `json_extract(metadata, '$."${jsonPathKey}"') = ?`;
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
sql,
|
|
327
|
+
needsValue: true,
|
|
328
|
+
transformValue: () => {
|
|
329
|
+
if (Array.isArray(value)) {
|
|
330
|
+
return [JSON.stringify(value)];
|
|
284
331
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
332
|
+
if (typeof value === 'object' && value !== null) {
|
|
333
|
+
return [JSON.stringify(value)];
|
|
334
|
+
}
|
|
335
|
+
if (typeof value === 'string') {
|
|
336
|
+
return [escapeLikePattern(value)];
|
|
337
|
+
}
|
|
338
|
+
return [value];
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
},
|
|
342
|
+
/**
|
|
343
|
+
* $objectContains: True JSON containment for advanced use (deep sub-object match).
|
|
344
|
+
* Usage: { field: { $objectContains: { ...subobject } } }
|
|
345
|
+
*/
|
|
346
|
+
// $objectContains: (key: string) => ({
|
|
347
|
+
// sql: '', // Will be overridden by transformValue
|
|
348
|
+
// needsValue: true,
|
|
349
|
+
// transformValue: (value: any) => ({
|
|
350
|
+
// sql: `json_type(json_extract(metadata, '$."${toJsonPathKey(key)}"')) = 'object'
|
|
351
|
+
// AND json_patch(json_extract(metadata, '$."${toJsonPathKey(key)}"'), ?) = json_extract(metadata, '$."${toJsonPathKey(key)}"')`,
|
|
352
|
+
// values: [JSON.stringify(value)],
|
|
353
|
+
// }),
|
|
354
|
+
// }),
|
|
296
355
|
};
|
|
297
356
|
|
|
298
|
-
|
|
357
|
+
interface FilterResult {
|
|
299
358
|
sql: string;
|
|
300
359
|
values: InValue[];
|
|
301
360
|
}
|
|
302
361
|
|
|
303
|
-
|
|
304
|
-
return
|
|
362
|
+
function isFilterResult(obj: any): obj is FilterResult {
|
|
363
|
+
return obj && typeof obj === 'object' && typeof obj.sql === 'string' && Array.isArray(obj.values);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const parseJsonPathKey = (key: string) => {
|
|
367
|
+
const parsedKey = parseFieldKey(key);
|
|
368
|
+
return parsedKey.replace(/\./g, '"."');
|
|
305
369
|
};
|
|
306
370
|
|
|
371
|
+
function escapeLikePattern(str: string): string {
|
|
372
|
+
return str.replace(/([%_\\])/g, '\\$1');
|
|
373
|
+
}
|
|
374
|
+
|
|
307
375
|
export function buildFilterQuery(filter: VectorFilter): FilterResult {
|
|
308
376
|
if (!filter) {
|
|
309
377
|
return { sql: '', values: [] };
|
|
@@ -418,13 +486,13 @@ function handleOperator(key: string, value: any): FilterResult {
|
|
|
418
486
|
operator === '$not'
|
|
419
487
|
? {
|
|
420
488
|
sql: `NOT (${Object.entries(operatorValue as Record<string, any>)
|
|
421
|
-
.map(([op, val]) => processOperator(key, op, val).sql)
|
|
489
|
+
.map(([op, val]) => processOperator(key, op as OperatorType, val).sql)
|
|
422
490
|
.join(' AND ')})`,
|
|
423
491
|
values: Object.entries(operatorValue as Record<string, any>).flatMap(
|
|
424
|
-
([op, val]) => processOperator(key, op, val).values,
|
|
492
|
+
([op, val]) => processOperator(key, op as OperatorType, val).values,
|
|
425
493
|
),
|
|
426
494
|
}
|
|
427
|
-
: processOperator(key, operator, operatorValue),
|
|
495
|
+
: processOperator(key, operator as OperatorType, operatorValue),
|
|
428
496
|
);
|
|
429
497
|
|
|
430
498
|
return {
|
|
@@ -435,10 +503,10 @@ function handleOperator(key: string, value: any): FilterResult {
|
|
|
435
503
|
|
|
436
504
|
// Handle single operator
|
|
437
505
|
const [[operator, operatorValue] = []] = Object.entries(value);
|
|
438
|
-
return processOperator(key, operator as
|
|
506
|
+
return processOperator(key, operator as OperatorType, operatorValue);
|
|
439
507
|
}
|
|
440
508
|
|
|
441
|
-
const processOperator = (key: string, operator:
|
|
509
|
+
const processOperator = (key: string, operator: OperatorType, operatorValue: any): FilterResult => {
|
|
442
510
|
if (!operator.startsWith('$') || !FILTER_OPERATORS[operator]) {
|
|
443
511
|
throw new Error(`Invalid operator: ${operator}`);
|
|
444
512
|
}
|
|
@@ -449,9 +517,9 @@ const processOperator = (key: string, operator: string, operatorValue: any): Fil
|
|
|
449
517
|
return { sql: operatorResult.sql, values: [] };
|
|
450
518
|
}
|
|
451
519
|
|
|
452
|
-
const transformed = operatorResult.transformValue ? operatorResult.transformValue(
|
|
520
|
+
const transformed = operatorResult.transformValue ? operatorResult.transformValue() : operatorValue;
|
|
453
521
|
|
|
454
|
-
if (transformed
|
|
522
|
+
if (isFilterResult(transformed)) {
|
|
455
523
|
return transformed;
|
|
456
524
|
}
|
|
457
525
|
|