@mastra/libsql 0.13.8-alpha.0 → 0.13.8-alpha.2
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/CHANGELOG.md +18 -0
- package/package.json +18 -5
- package/.turbo/turbo-build.log +0 -4
- package/eslint.config.js +0 -6
- package/src/index.ts +0 -3
- package/src/storage/domains/legacy-evals/index.ts +0 -149
- package/src/storage/domains/memory/index.ts +0 -937
- package/src/storage/domains/observability/index.ts +0 -237
- package/src/storage/domains/operations/index.ts +0 -506
- package/src/storage/domains/scores/index.ts +0 -226
- package/src/storage/domains/traces/index.ts +0 -150
- package/src/storage/domains/utils.ts +0 -272
- package/src/storage/domains/workflows/index.ts +0 -421
- package/src/storage/index.test.ts +0 -16
- package/src/storage/index.ts +0 -499
- package/src/vector/filter.test.ts +0 -906
- package/src/vector/filter.ts +0 -131
- package/src/vector/index.test.ts +0 -1693
- package/src/vector/index.ts +0 -515
- package/src/vector/prompt.ts +0 -101
- package/src/vector/sql-builder.ts +0 -548
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -5
- package/tsup.config.ts +0 -17
- package/vitest.config.ts +0 -11
|
@@ -1,548 +0,0 @@
|
|
|
1
|
-
import type { InValue } from '@libsql/client';
|
|
2
|
-
import { parseFieldKey } from '@mastra/core/utils';
|
|
3
|
-
import type {
|
|
4
|
-
BasicOperator,
|
|
5
|
-
NumericOperator,
|
|
6
|
-
ArrayOperator,
|
|
7
|
-
ElementOperator,
|
|
8
|
-
LogicalOperator,
|
|
9
|
-
} from '@mastra/core/vector/filter';
|
|
10
|
-
import type { LibSQLVectorFilter } from './filter';
|
|
11
|
-
|
|
12
|
-
type OperatorType =
|
|
13
|
-
| BasicOperator
|
|
14
|
-
| NumericOperator
|
|
15
|
-
| ArrayOperator
|
|
16
|
-
| ElementOperator
|
|
17
|
-
| LogicalOperator
|
|
18
|
-
| '$contains'
|
|
19
|
-
| '$size';
|
|
20
|
-
|
|
21
|
-
type FilterOperator = {
|
|
22
|
-
sql: string;
|
|
23
|
-
needsValue: boolean;
|
|
24
|
-
transformValue?: () => any;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
type OperatorFn = (key: string, value?: any) => FilterOperator;
|
|
28
|
-
|
|
29
|
-
// Helper functions to create operators
|
|
30
|
-
const createBasicOperator = (symbol: string) => {
|
|
31
|
-
return (key: string, value: any): FilterOperator => {
|
|
32
|
-
const jsonPath = getJsonPath(key);
|
|
33
|
-
return {
|
|
34
|
-
sql: `CASE
|
|
35
|
-
WHEN ? IS NULL THEN json_extract(metadata, ${jsonPath}) IS ${symbol === '=' ? '' : 'NOT'} NULL
|
|
36
|
-
ELSE json_extract(metadata, ${jsonPath}) ${symbol} ?
|
|
37
|
-
END`,
|
|
38
|
-
needsValue: true,
|
|
39
|
-
transformValue: () => {
|
|
40
|
-
// Return the values directly, not in an object
|
|
41
|
-
return [value, value];
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
const createNumericOperator = (symbol: string) => {
|
|
47
|
-
return (key: string): FilterOperator => {
|
|
48
|
-
const jsonPath = getJsonPath(key);
|
|
49
|
-
return {
|
|
50
|
-
sql: `CAST(json_extract(metadata, ${jsonPath}) AS NUMERIC) ${symbol} ?`,
|
|
51
|
-
needsValue: true,
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const validateJsonArray = (key: string) => {
|
|
57
|
-
const jsonPath = getJsonPath(key);
|
|
58
|
-
return `json_valid(json_extract(metadata, ${jsonPath}))
|
|
59
|
-
AND json_type(json_extract(metadata, ${jsonPath})) = 'array'`;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const pattern = /json_extract\(metadata, '\$\.(?:"[^"]*"(?:\."[^"]*")*|[^']+)'\)/g;
|
|
63
|
-
|
|
64
|
-
function buildElemMatchConditions(value: any) {
|
|
65
|
-
const conditions = Object.entries(value).map(([field, fieldValue]) => {
|
|
66
|
-
if (field.startsWith('$')) {
|
|
67
|
-
// Direct operators on array elements ($in, $gt, etc)
|
|
68
|
-
const { sql, values } = buildCondition('elem.value', { [field]: fieldValue }, '');
|
|
69
|
-
// Replace the metadata path with elem.value
|
|
70
|
-
const elemSql = sql.replace(pattern, 'elem.value');
|
|
71
|
-
return { sql: elemSql, values };
|
|
72
|
-
} else if (typeof fieldValue === 'object' && !Array.isArray(fieldValue)) {
|
|
73
|
-
// Nested field with operators (count: { $gt: 20 })
|
|
74
|
-
const { sql, values } = buildCondition(field, fieldValue, '');
|
|
75
|
-
// Replace the field path with elem.value path
|
|
76
|
-
const jsonPath = parseJsonPathKey(field);
|
|
77
|
-
const elemSql = sql.replace(pattern, `json_extract(elem.value, '$.${jsonPath}')`);
|
|
78
|
-
return { sql: elemSql, values };
|
|
79
|
-
} else {
|
|
80
|
-
const jsonPath = parseJsonPathKey(field);
|
|
81
|
-
// Simple field equality (warehouse: 'A')
|
|
82
|
-
return {
|
|
83
|
-
sql: `json_extract(elem.value, '$.${jsonPath}') = ?`,
|
|
84
|
-
values: [fieldValue],
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return conditions;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Define all filter operators
|
|
93
|
-
const FILTER_OPERATORS: Record<OperatorType, OperatorFn> = {
|
|
94
|
-
$eq: createBasicOperator('='),
|
|
95
|
-
$ne: createBasicOperator('!='),
|
|
96
|
-
$gt: createNumericOperator('>'),
|
|
97
|
-
$gte: createNumericOperator('>='),
|
|
98
|
-
$lt: createNumericOperator('<'),
|
|
99
|
-
$lte: createNumericOperator('<='),
|
|
100
|
-
|
|
101
|
-
// Array Operators
|
|
102
|
-
$in: (key: string, value: any) => {
|
|
103
|
-
const jsonPath = getJsonPath(key);
|
|
104
|
-
const arr = Array.isArray(value) ? value : [value];
|
|
105
|
-
if (arr.length === 0) {
|
|
106
|
-
return { sql: '1 = 0', needsValue: true, transformValue: () => [] };
|
|
107
|
-
}
|
|
108
|
-
const paramPlaceholders = arr.map(() => '?').join(',');
|
|
109
|
-
return {
|
|
110
|
-
sql: `(
|
|
111
|
-
CASE
|
|
112
|
-
WHEN ${validateJsonArray(key)} THEN
|
|
113
|
-
EXISTS (
|
|
114
|
-
SELECT 1 FROM json_each(json_extract(metadata, ${jsonPath})) as elem
|
|
115
|
-
WHERE elem.value IN (SELECT value FROM json_each(?))
|
|
116
|
-
)
|
|
117
|
-
ELSE json_extract(metadata, ${jsonPath}) IN (${paramPlaceholders})
|
|
118
|
-
END
|
|
119
|
-
)`,
|
|
120
|
-
needsValue: true,
|
|
121
|
-
transformValue: () => [JSON.stringify(arr), ...arr],
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
|
|
125
|
-
$nin: (key: string, value: any) => {
|
|
126
|
-
const jsonPath = getJsonPath(key);
|
|
127
|
-
const arr = Array.isArray(value) ? value : [value];
|
|
128
|
-
if (arr.length === 0) {
|
|
129
|
-
return { sql: '1 = 1', needsValue: true, transformValue: () => [] };
|
|
130
|
-
}
|
|
131
|
-
const paramPlaceholders = arr.map(() => '?').join(',');
|
|
132
|
-
return {
|
|
133
|
-
sql: `(
|
|
134
|
-
CASE
|
|
135
|
-
WHEN ${validateJsonArray(key)} THEN
|
|
136
|
-
NOT EXISTS (
|
|
137
|
-
SELECT 1 FROM json_each(json_extract(metadata, ${jsonPath})) as elem
|
|
138
|
-
WHERE elem.value IN (SELECT value FROM json_each(?))
|
|
139
|
-
)
|
|
140
|
-
ELSE json_extract(metadata, ${jsonPath}) NOT IN (${paramPlaceholders})
|
|
141
|
-
END
|
|
142
|
-
)`,
|
|
143
|
-
needsValue: true,
|
|
144
|
-
transformValue: () => [JSON.stringify(arr), ...arr],
|
|
145
|
-
};
|
|
146
|
-
},
|
|
147
|
-
$all: (key: string, value: any) => {
|
|
148
|
-
const jsonPath = getJsonPath(key);
|
|
149
|
-
let sql: string;
|
|
150
|
-
const arrayValue = Array.isArray(value) ? value : [value];
|
|
151
|
-
|
|
152
|
-
if (arrayValue.length === 0) {
|
|
153
|
-
// If the array is empty, always return false (no matches)
|
|
154
|
-
sql = '1 = 0';
|
|
155
|
-
} else {
|
|
156
|
-
sql = `(
|
|
157
|
-
CASE
|
|
158
|
-
WHEN ${validateJsonArray(key)} THEN
|
|
159
|
-
NOT EXISTS (
|
|
160
|
-
SELECT value
|
|
161
|
-
FROM json_each(?)
|
|
162
|
-
WHERE value NOT IN (
|
|
163
|
-
SELECT value
|
|
164
|
-
FROM json_each(json_extract(metadata, ${jsonPath}))
|
|
165
|
-
)
|
|
166
|
-
)
|
|
167
|
-
ELSE FALSE
|
|
168
|
-
END
|
|
169
|
-
)`;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
sql,
|
|
174
|
-
needsValue: true,
|
|
175
|
-
transformValue: () => {
|
|
176
|
-
if (arrayValue.length === 0) {
|
|
177
|
-
return [];
|
|
178
|
-
}
|
|
179
|
-
return [JSON.stringify(arrayValue)];
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
},
|
|
183
|
-
$elemMatch: (key: string, value: any) => {
|
|
184
|
-
const jsonPath = getJsonPath(key);
|
|
185
|
-
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
186
|
-
throw new Error('$elemMatch requires an object with conditions');
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// For nested object conditions
|
|
190
|
-
const conditions = buildElemMatchConditions(value);
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
sql: `(
|
|
194
|
-
CASE
|
|
195
|
-
WHEN ${validateJsonArray(key)} THEN
|
|
196
|
-
EXISTS (
|
|
197
|
-
SELECT 1
|
|
198
|
-
FROM json_each(json_extract(metadata, ${jsonPath})) as elem
|
|
199
|
-
WHERE ${conditions.map(c => c.sql).join(' AND ')}
|
|
200
|
-
)
|
|
201
|
-
ELSE FALSE
|
|
202
|
-
END
|
|
203
|
-
)`,
|
|
204
|
-
needsValue: true,
|
|
205
|
-
transformValue: () => conditions.flatMap(c => c.values),
|
|
206
|
-
};
|
|
207
|
-
},
|
|
208
|
-
|
|
209
|
-
// Element Operators
|
|
210
|
-
$exists: (key: string) => {
|
|
211
|
-
const jsonPath = getJsonPath(key);
|
|
212
|
-
return {
|
|
213
|
-
sql: `json_extract(metadata, ${jsonPath}) IS NOT NULL`,
|
|
214
|
-
needsValue: false,
|
|
215
|
-
};
|
|
216
|
-
},
|
|
217
|
-
|
|
218
|
-
// Logical Operators
|
|
219
|
-
$and: (key: string) => ({
|
|
220
|
-
sql: `(${key})`,
|
|
221
|
-
needsValue: false,
|
|
222
|
-
}),
|
|
223
|
-
$or: (key: string) => ({
|
|
224
|
-
sql: `(${key})`,
|
|
225
|
-
needsValue: false,
|
|
226
|
-
}),
|
|
227
|
-
$not: key => ({ sql: `NOT (${key})`, needsValue: false }),
|
|
228
|
-
$nor: (key: string) => ({
|
|
229
|
-
sql: `NOT (${key})`,
|
|
230
|
-
needsValue: false,
|
|
231
|
-
}),
|
|
232
|
-
$size: (key: string, paramIndex: number) => {
|
|
233
|
-
const jsonPath = getJsonPath(key);
|
|
234
|
-
return {
|
|
235
|
-
sql: `(
|
|
236
|
-
CASE
|
|
237
|
-
WHEN json_type(json_extract(metadata, ${jsonPath})) = 'array' THEN
|
|
238
|
-
json_array_length(json_extract(metadata, ${jsonPath})) = $${paramIndex}
|
|
239
|
-
ELSE FALSE
|
|
240
|
-
END
|
|
241
|
-
)`,
|
|
242
|
-
needsValue: true,
|
|
243
|
-
};
|
|
244
|
-
},
|
|
245
|
-
// /**
|
|
246
|
-
// * Regex Operators
|
|
247
|
-
// * Supports case insensitive and multiline
|
|
248
|
-
// */
|
|
249
|
-
// $regex: (key: string): FilterOperator => ({
|
|
250
|
-
// sql: `json_extract(metadata, '$."${toJsonPathKey(key)}"') = ?`,
|
|
251
|
-
// needsValue: true,
|
|
252
|
-
// transformValue: (value: any) => {
|
|
253
|
-
// const pattern = typeof value === 'object' ? value.$regex : value;
|
|
254
|
-
// const options = typeof value === 'object' ? value.$options || '' : '';
|
|
255
|
-
// let sql = `json_extract(metadata, '$."${toJsonPathKey(key)}"')`;
|
|
256
|
-
|
|
257
|
-
// // Handle multiline
|
|
258
|
-
// // if (options.includes('m')) {
|
|
259
|
-
// // sql = `REPLACE(${sql}, CHAR(10), '\n')`;
|
|
260
|
-
// // }
|
|
261
|
-
|
|
262
|
-
// // let finalPattern = pattern;
|
|
263
|
-
// // if (options) {
|
|
264
|
-
// // finalPattern = `(\\?${options})${pattern}`;
|
|
265
|
-
// // }
|
|
266
|
-
|
|
267
|
-
// // // Handle case insensitivity
|
|
268
|
-
// // if (options.includes('i')) {
|
|
269
|
-
// // sql = `LOWER(${sql}) REGEXP LOWER(?)`;
|
|
270
|
-
// // } else {
|
|
271
|
-
// // sql = `${sql} REGEXP ?`;
|
|
272
|
-
// // }
|
|
273
|
-
|
|
274
|
-
// if (options.includes('m')) {
|
|
275
|
-
// sql = `EXISTS (
|
|
276
|
-
// SELECT 1
|
|
277
|
-
// FROM json_each(
|
|
278
|
-
// json_array(
|
|
279
|
-
// ${sql},
|
|
280
|
-
// REPLACE(${sql}, CHAR(10), CHAR(13))
|
|
281
|
-
// )
|
|
282
|
-
// ) as lines
|
|
283
|
-
// WHERE lines.value REGEXP ?
|
|
284
|
-
// )`;
|
|
285
|
-
// } else {
|
|
286
|
-
// sql = `${sql} REGEXP ?`;
|
|
287
|
-
// }
|
|
288
|
-
|
|
289
|
-
// // Handle case insensitivity
|
|
290
|
-
// if (options.includes('i')) {
|
|
291
|
-
// sql = sql.replace('REGEXP ?', 'REGEXP LOWER(?)');
|
|
292
|
-
// sql = sql.replace('value REGEXP', 'LOWER(value) REGEXP');
|
|
293
|
-
// }
|
|
294
|
-
|
|
295
|
-
// // Handle extended - allows whitespace and comments in pattern
|
|
296
|
-
// if (options.includes('x')) {
|
|
297
|
-
// // Remove whitespace and comments from pattern
|
|
298
|
-
// const cleanPattern = pattern.replace(/\s+|#.*$/gm, '');
|
|
299
|
-
// return {
|
|
300
|
-
// sql,
|
|
301
|
-
// values: [cleanPattern],
|
|
302
|
-
// };
|
|
303
|
-
// }
|
|
304
|
-
|
|
305
|
-
// return {
|
|
306
|
-
// sql,
|
|
307
|
-
// values: [pattern],
|
|
308
|
-
// };
|
|
309
|
-
// },
|
|
310
|
-
// }),
|
|
311
|
-
$contains: (key: string, value: any) => {
|
|
312
|
-
const jsonPathKey = parseJsonPathKey(key);
|
|
313
|
-
let sql;
|
|
314
|
-
if (Array.isArray(value)) {
|
|
315
|
-
sql = `(
|
|
316
|
-
SELECT ${validateJsonArray(jsonPathKey)}
|
|
317
|
-
AND EXISTS (
|
|
318
|
-
SELECT 1
|
|
319
|
-
FROM json_each(json_extract(metadata, '$."${jsonPathKey}"')) as m
|
|
320
|
-
WHERE m.value IN (SELECT value FROM json_each(?))
|
|
321
|
-
)
|
|
322
|
-
)`;
|
|
323
|
-
} else if (typeof value === 'string') {
|
|
324
|
-
sql = `lower(json_extract(metadata, '$."${jsonPathKey}"')) LIKE '%' || lower(?) || '%' ESCAPE '\\'`;
|
|
325
|
-
} else {
|
|
326
|
-
sql = `json_extract(metadata, '$."${jsonPathKey}"') = ?`;
|
|
327
|
-
}
|
|
328
|
-
return {
|
|
329
|
-
sql,
|
|
330
|
-
needsValue: true,
|
|
331
|
-
transformValue: () => {
|
|
332
|
-
if (Array.isArray(value)) {
|
|
333
|
-
return [JSON.stringify(value)];
|
|
334
|
-
}
|
|
335
|
-
if (typeof value === 'object' && value !== null) {
|
|
336
|
-
return [JSON.stringify(value)];
|
|
337
|
-
}
|
|
338
|
-
if (typeof value === 'string') {
|
|
339
|
-
return [escapeLikePattern(value)];
|
|
340
|
-
}
|
|
341
|
-
return [value];
|
|
342
|
-
},
|
|
343
|
-
};
|
|
344
|
-
},
|
|
345
|
-
/**
|
|
346
|
-
* $objectContains: True JSON containment for advanced use (deep sub-object match).
|
|
347
|
-
* Usage: { field: { $objectContains: { ...subobject } } }
|
|
348
|
-
*/
|
|
349
|
-
// $objectContains: (key: string) => ({
|
|
350
|
-
// sql: '', // Will be overridden by transformValue
|
|
351
|
-
// needsValue: true,
|
|
352
|
-
// transformValue: (value: any) => ({
|
|
353
|
-
// sql: `json_type(json_extract(metadata, '$."${toJsonPathKey(key)}"')) = 'object'
|
|
354
|
-
// AND json_patch(json_extract(metadata, '$."${toJsonPathKey(key)}"'), ?) = json_extract(metadata, '$."${toJsonPathKey(key)}"')`,
|
|
355
|
-
// values: [JSON.stringify(value)],
|
|
356
|
-
// }),
|
|
357
|
-
// }),
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
interface FilterResult {
|
|
361
|
-
sql: string;
|
|
362
|
-
values: InValue[];
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function isFilterResult(obj: any): obj is FilterResult {
|
|
366
|
-
return obj && typeof obj === 'object' && typeof obj.sql === 'string' && Array.isArray(obj.values);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const parseJsonPathKey = (key: string) => {
|
|
370
|
-
const parsedKey = parseFieldKey(key);
|
|
371
|
-
// Only add quotes around path segments if they contain dots
|
|
372
|
-
if (parsedKey.includes('.')) {
|
|
373
|
-
return parsedKey
|
|
374
|
-
.split('.')
|
|
375
|
-
.map(segment => `"${segment}"`)
|
|
376
|
-
.join('.');
|
|
377
|
-
}
|
|
378
|
-
return parsedKey;
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
// Helper to generate the correct JSON path format for LibSQL
|
|
382
|
-
const getJsonPath = (key: string) => {
|
|
383
|
-
const jsonPathKey = parseJsonPathKey(key);
|
|
384
|
-
// Always use quotes for consistency
|
|
385
|
-
return `'$.${jsonPathKey}'`;
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
function escapeLikePattern(str: string): string {
|
|
389
|
-
return str.replace(/([%_\\])/g, '\\$1');
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
export function buildFilterQuery(filter: LibSQLVectorFilter): FilterResult {
|
|
393
|
-
if (!filter) {
|
|
394
|
-
return { sql: '', values: [] };
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const values: InValue[] = [];
|
|
398
|
-
const conditions = Object.entries(filter)
|
|
399
|
-
.map(([key, value]) => {
|
|
400
|
-
const condition = buildCondition(key, value, '');
|
|
401
|
-
values.push(...condition.values);
|
|
402
|
-
return condition.sql;
|
|
403
|
-
})
|
|
404
|
-
.join(' AND ');
|
|
405
|
-
|
|
406
|
-
return {
|
|
407
|
-
sql: conditions ? `WHERE ${conditions}` : '',
|
|
408
|
-
values,
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function buildCondition(key: string, value: any, parentPath: string): FilterResult {
|
|
413
|
-
// Handle logical operators ($and/$or)
|
|
414
|
-
if (['$and', '$or', '$not', '$nor'].includes(key)) {
|
|
415
|
-
return handleLogicalOperator(key as '$and' | '$or' | '$not' | '$nor', value, parentPath);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// If condition is not a FilterCondition object, assume it's an equality check
|
|
419
|
-
if (!value || typeof value !== 'object') {
|
|
420
|
-
const jsonPath = getJsonPath(key);
|
|
421
|
-
return {
|
|
422
|
-
sql: `json_extract(metadata, ${jsonPath}) = ?`,
|
|
423
|
-
values: [value],
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
//TODO: Add regex support
|
|
428
|
-
// if ('$regex' in value) {
|
|
429
|
-
// return handleRegexOperator(key, value);
|
|
430
|
-
// }
|
|
431
|
-
|
|
432
|
-
// Handle operator conditions
|
|
433
|
-
return handleOperator(key, value);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// function handleRegexOperator(key: string, value: any): FilterResult {
|
|
437
|
-
// const operatorFn = FILTER_OPERATORS['$regex']!;
|
|
438
|
-
// const operatorResult = operatorFn(key, value);
|
|
439
|
-
// const transformed = operatorResult.transformValue ? operatorResult.transformValue(value) : value;
|
|
440
|
-
|
|
441
|
-
// return {
|
|
442
|
-
// sql: transformed.sql,
|
|
443
|
-
// values: transformed.values,
|
|
444
|
-
// };
|
|
445
|
-
// }
|
|
446
|
-
|
|
447
|
-
function handleLogicalOperator(
|
|
448
|
-
key: '$and' | '$or' | '$not' | '$nor',
|
|
449
|
-
value: LibSQLVectorFilter[] | LibSQLVectorFilter,
|
|
450
|
-
parentPath: string,
|
|
451
|
-
): FilterResult {
|
|
452
|
-
// Handle empty conditions
|
|
453
|
-
if (!value || (Array.isArray(value) && value.length === 0)) {
|
|
454
|
-
switch (key) {
|
|
455
|
-
case '$and':
|
|
456
|
-
case '$nor':
|
|
457
|
-
return { sql: 'true', values: [] };
|
|
458
|
-
case '$or':
|
|
459
|
-
return { sql: 'false', values: [] };
|
|
460
|
-
case '$not':
|
|
461
|
-
throw new Error('$not operator cannot be empty');
|
|
462
|
-
default:
|
|
463
|
-
return { sql: 'true', values: [] };
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (key === '$not') {
|
|
468
|
-
// For top-level $not
|
|
469
|
-
const entries = Object.entries(value);
|
|
470
|
-
const conditions = entries.map(([fieldKey, fieldValue]) => buildCondition(fieldKey, fieldValue, key));
|
|
471
|
-
return {
|
|
472
|
-
sql: `NOT (${conditions.map(c => c.sql).join(' AND ')})`,
|
|
473
|
-
values: conditions.flatMap(c => c.values),
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const values: InValue[] = [];
|
|
478
|
-
const joinOperator = key === '$or' || key === '$nor' ? 'OR' : 'AND';
|
|
479
|
-
const conditions = Array.isArray(value)
|
|
480
|
-
? value.map(f => {
|
|
481
|
-
const entries = !!f ? Object.entries(f) : [];
|
|
482
|
-
return entries.map(([k, v]) => buildCondition(k, v, key));
|
|
483
|
-
})
|
|
484
|
-
: [buildCondition(key, value, parentPath)];
|
|
485
|
-
|
|
486
|
-
const joined = conditions
|
|
487
|
-
.flat()
|
|
488
|
-
.map(c => {
|
|
489
|
-
values.push(...c.values);
|
|
490
|
-
return c.sql;
|
|
491
|
-
})
|
|
492
|
-
.join(` ${joinOperator} `);
|
|
493
|
-
|
|
494
|
-
return {
|
|
495
|
-
sql: key === '$nor' ? `NOT (${joined})` : `(${joined})`,
|
|
496
|
-
values,
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function handleOperator(key: string, value: any): FilterResult {
|
|
501
|
-
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
502
|
-
const entries = Object.entries(value);
|
|
503
|
-
const results = entries.map(([operator, operatorValue]) =>
|
|
504
|
-
operator === '$not'
|
|
505
|
-
? {
|
|
506
|
-
sql: `NOT (${Object.entries(operatorValue as Record<string, any>)
|
|
507
|
-
.map(([op, val]) => processOperator(key, op as OperatorType, val).sql)
|
|
508
|
-
.join(' AND ')})`,
|
|
509
|
-
values: Object.entries(operatorValue as Record<string, any>).flatMap(
|
|
510
|
-
([op, val]) => processOperator(key, op as OperatorType, val).values,
|
|
511
|
-
),
|
|
512
|
-
}
|
|
513
|
-
: processOperator(key, operator as OperatorType, operatorValue),
|
|
514
|
-
);
|
|
515
|
-
|
|
516
|
-
return {
|
|
517
|
-
sql: `(${results.map(r => r.sql).join(' AND ')})`,
|
|
518
|
-
values: results.flatMap(r => r.values),
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Handle single operator
|
|
523
|
-
const [[operator, operatorValue] = []] = Object.entries(value);
|
|
524
|
-
return processOperator(key, operator as OperatorType, operatorValue);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
const processOperator = (key: string, operator: OperatorType, operatorValue: any): FilterResult => {
|
|
528
|
-
if (!operator.startsWith('$') || !FILTER_OPERATORS[operator]) {
|
|
529
|
-
throw new Error(`Invalid operator: ${operator}`);
|
|
530
|
-
}
|
|
531
|
-
const operatorFn = FILTER_OPERATORS[operator]!;
|
|
532
|
-
const operatorResult = operatorFn(key, operatorValue);
|
|
533
|
-
|
|
534
|
-
if (!operatorResult.needsValue) {
|
|
535
|
-
return { sql: operatorResult.sql, values: [] };
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
const transformed = operatorResult.transformValue ? operatorResult.transformValue() : operatorValue;
|
|
539
|
-
|
|
540
|
-
if (isFilterResult(transformed)) {
|
|
541
|
-
return transformed;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
return {
|
|
545
|
-
sql: operatorResult.sql,
|
|
546
|
-
values: Array.isArray(transformed) ? transformed : [transformed],
|
|
547
|
-
};
|
|
548
|
-
};
|
package/tsconfig.build.json
DELETED
package/tsconfig.json
DELETED
package/tsup.config.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { generateTypes } from '@internal/types-builder';
|
|
2
|
-
import { defineConfig } from 'tsup';
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
entry: ['src/index.ts'],
|
|
6
|
-
format: ['esm', 'cjs'],
|
|
7
|
-
clean: true,
|
|
8
|
-
dts: false,
|
|
9
|
-
splitting: true,
|
|
10
|
-
treeshake: {
|
|
11
|
-
preset: 'smallest',
|
|
12
|
-
},
|
|
13
|
-
sourcemap: true,
|
|
14
|
-
onSuccess: async () => {
|
|
15
|
-
await generateTypes(process.cwd());
|
|
16
|
-
},
|
|
17
|
-
});
|