@mastra/mongodb 0.13.3 → 0.13.6-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/CHANGELOG.md +27 -0
- package/dist/index.cjs +32 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +19 -1
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +19 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +19 -6
- package/.turbo/turbo-build.log +0 -4
- package/docker-compose.yaml +0 -30
- package/eslint.config.js +0 -6
- package/src/index.ts +0 -4
- package/src/storage/MongoDBConnector.ts +0 -93
- package/src/storage/connectors/MongoDBConnector.ts +0 -93
- package/src/storage/connectors/base.ts +0 -7
- package/src/storage/domains/legacy-evals/index.ts +0 -193
- package/src/storage/domains/memory/index.ts +0 -786
- package/src/storage/domains/operations/index.ts +0 -155
- package/src/storage/domains/scores/index.ts +0 -389
- package/src/storage/domains/traces/index.ts +0 -142
- package/src/storage/domains/utils.ts +0 -43
- package/src/storage/domains/workflows/index.ts +0 -196
- package/src/storage/index.test.ts +0 -62
- package/src/storage/index.ts +0 -406
- package/src/storage/types.ts +0 -14
- package/src/vector/filter.test.ts +0 -420
- package/src/vector/filter.ts +0 -143
- package/src/vector/index.test.ts +0 -651
- package/src/vector/index.ts +0 -656
- package/src/vector/prompt.ts +0 -97
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -5
- package/tsup.config.ts +0 -17
- package/vitest.config.ts +0 -11
package/src/storage/types.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { MongoClientOptions } from 'mongodb';
|
|
2
|
-
import type { ConnectorHandler } from './connectors/base';
|
|
3
|
-
|
|
4
|
-
export type MongoDBConfig =
|
|
5
|
-
| DatabaseConfig
|
|
6
|
-
| {
|
|
7
|
-
connectorHandler: ConnectorHandler;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export type DatabaseConfig = {
|
|
11
|
-
url: string;
|
|
12
|
-
dbName: string;
|
|
13
|
-
options?: MongoClientOptions;
|
|
14
|
-
};
|
|
@@ -1,420 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import type { MongoDBVectorFilter } from './filter';
|
|
4
|
-
import { MongoDBFilterTranslator } from './filter';
|
|
5
|
-
|
|
6
|
-
describe('MongoDBFilterTranslator', () => {
|
|
7
|
-
let translator: MongoDBFilterTranslator;
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
translator = new MongoDBFilterTranslator();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
// Basic Filter Operations
|
|
14
|
-
describe('basic operations', () => {
|
|
15
|
-
it('handles simple equality', () => {
|
|
16
|
-
const filter: MongoDBVectorFilter = { field: 'value' };
|
|
17
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('handles comparison operators', () => {
|
|
21
|
-
const filter: MongoDBVectorFilter = {
|
|
22
|
-
age: { $gt: 25 },
|
|
23
|
-
score: { $lte: 100 },
|
|
24
|
-
};
|
|
25
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('handles valid multiple operators on same field', () => {
|
|
29
|
-
const filter: MongoDBVectorFilter = {
|
|
30
|
-
price: { $gt: 100, $lt: 200 },
|
|
31
|
-
quantity: { $gte: 10, $lte: 20 },
|
|
32
|
-
};
|
|
33
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('handles null values correctly', () => {
|
|
37
|
-
const filter: MongoDBVectorFilter = {
|
|
38
|
-
field: null,
|
|
39
|
-
other: { $eq: null },
|
|
40
|
-
};
|
|
41
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('handles boolean values correctly', () => {
|
|
45
|
-
const filter: MongoDBVectorFilter = {
|
|
46
|
-
active: true,
|
|
47
|
-
deleted: false,
|
|
48
|
-
status: { $eq: true },
|
|
49
|
-
};
|
|
50
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// Array Operations
|
|
55
|
-
describe('array operations', () => {
|
|
56
|
-
it('handles array operators', () => {
|
|
57
|
-
const filter: MongoDBVectorFilter = {
|
|
58
|
-
tags: { $all: ['tag1', 'tag2'] },
|
|
59
|
-
categories: { $in: ['A', 'B'] },
|
|
60
|
-
items: { $nin: ['item1', 'item2'] },
|
|
61
|
-
scores: { $elemMatch: { $gt: 90 } },
|
|
62
|
-
};
|
|
63
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('handles empty array values', () => {
|
|
67
|
-
const filter: MongoDBVectorFilter = {
|
|
68
|
-
tags: { $in: [] },
|
|
69
|
-
categories: { $all: [] },
|
|
70
|
-
};
|
|
71
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('handles nested array operators', () => {
|
|
75
|
-
const filter: MongoDBVectorFilter = {
|
|
76
|
-
$and: [{ tags: { $all: ['tag1', 'tag2'] } }, { 'nested.array': { $in: [1, 2, 3] } }],
|
|
77
|
-
};
|
|
78
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('handles $size operator', () => {
|
|
82
|
-
const filter: MongoDBVectorFilter = {
|
|
83
|
-
tags: { $size: 3 },
|
|
84
|
-
};
|
|
85
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Logical Operators
|
|
90
|
-
describe('logical operators', () => {
|
|
91
|
-
it('handles logical operators', () => {
|
|
92
|
-
const filter: MongoDBVectorFilter = {
|
|
93
|
-
$or: [{ status: 'active' }, { age: { $gt: 25 } }],
|
|
94
|
-
};
|
|
95
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('handles $not operator', () => {
|
|
99
|
-
const filter: MongoDBVectorFilter = {
|
|
100
|
-
field: { $not: { $eq: 'value' } },
|
|
101
|
-
$not: { field: 'value' },
|
|
102
|
-
};
|
|
103
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('handles $nor operator', () => {
|
|
107
|
-
const filter: MongoDBVectorFilter = {
|
|
108
|
-
$nor: [{ status: 'deleted' }, { active: false }],
|
|
109
|
-
};
|
|
110
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('handles nested logical operators', () => {
|
|
114
|
-
const filter: MongoDBVectorFilter = {
|
|
115
|
-
$and: [
|
|
116
|
-
{ status: 'active' },
|
|
117
|
-
{ $or: [{ category: { $in: ['A', 'B'] } }, { $and: [{ price: { $gt: 100 } }, { stock: { $lt: 50 } }] }] },
|
|
118
|
-
],
|
|
119
|
-
};
|
|
120
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('handles empty conditions in logical operators', () => {
|
|
124
|
-
const filter: MongoDBVectorFilter = {
|
|
125
|
-
$and: [],
|
|
126
|
-
$or: [{}],
|
|
127
|
-
field: 'value',
|
|
128
|
-
};
|
|
129
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('allows multiple logical operators at root level with complex conditions', () => {
|
|
133
|
-
expect(() =>
|
|
134
|
-
translator.translate({
|
|
135
|
-
$and: [{ field1: { $gt: 10 } }],
|
|
136
|
-
$or: [{ field2: { $lt: 20 } }],
|
|
137
|
-
field4: { $not: { $eq: 'value' } },
|
|
138
|
-
}),
|
|
139
|
-
).not.toThrow();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('allows logical operators at root level', () => {
|
|
143
|
-
expect(() =>
|
|
144
|
-
translator.translate({
|
|
145
|
-
$and: [{ field1: 'value1' }, { field2: 'value2' }],
|
|
146
|
-
$or: [{ field3: 'value3' }, { field4: 'value4' }],
|
|
147
|
-
}),
|
|
148
|
-
).not.toThrow();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('allows logical operators nested within other logical operators', () => {
|
|
152
|
-
expect(() =>
|
|
153
|
-
translator.translate({
|
|
154
|
-
$and: [
|
|
155
|
-
{
|
|
156
|
-
$or: [{ field1: 'value1' }, { field2: 'value2' }],
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
$and: [{ field3: 'value3' }, { field4: 'value4' }],
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
}),
|
|
163
|
-
).not.toThrow();
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// Logical Operator Validation
|
|
168
|
-
describe('logical operator validation', () => {
|
|
169
|
-
it('throws error for direct operators in logical operator arrays', () => {
|
|
170
|
-
expect(() =>
|
|
171
|
-
translator.translate({
|
|
172
|
-
$and: [{ $eq: 'value' }, { $gt: 100 }],
|
|
173
|
-
}),
|
|
174
|
-
).toThrow(/Logical operators must contain field conditions/);
|
|
175
|
-
|
|
176
|
-
expect(() =>
|
|
177
|
-
translator.translate({
|
|
178
|
-
$or: [{ $in: ['value1', 'value2'] }],
|
|
179
|
-
} as any),
|
|
180
|
-
).toThrow(/Logical operators must contain field conditions/);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('throws error for deeply nested logical operators in non-logical contexts', () => {
|
|
184
|
-
expect(() =>
|
|
185
|
-
translator.translate({
|
|
186
|
-
field: {
|
|
187
|
-
$gt: {
|
|
188
|
-
$or: [{ subfield: 'value1' }, { subfield: 'value2' }],
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
} as any),
|
|
192
|
-
).toThrow();
|
|
193
|
-
|
|
194
|
-
expect(() =>
|
|
195
|
-
translator.translate({
|
|
196
|
-
field: {
|
|
197
|
-
$in: [
|
|
198
|
-
{
|
|
199
|
-
$and: [{ subfield: 'value1' }, { subfield: 'value2' }],
|
|
200
|
-
} as any,
|
|
201
|
-
],
|
|
202
|
-
},
|
|
203
|
-
}),
|
|
204
|
-
).toThrow();
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('throws error for logical operators nested in non-logical contexts', () => {
|
|
208
|
-
expect(() =>
|
|
209
|
-
translator.translate({
|
|
210
|
-
field: {
|
|
211
|
-
$gt: {
|
|
212
|
-
$or: [{ subfield: 'value1' }, { subfield: 'value2' }],
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
} as any),
|
|
216
|
-
).toThrow();
|
|
217
|
-
|
|
218
|
-
expect(() =>
|
|
219
|
-
translator.translate({
|
|
220
|
-
field: {
|
|
221
|
-
$not: {
|
|
222
|
-
$and: [{ subfield: 'value1' }, { subfield: 'value2' }],
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
}),
|
|
226
|
-
).not.toThrow(); // $not is allowed to contain logical operators
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('throws error for $not if not an object', () => {
|
|
230
|
-
expect(() => translator.translate({ $not: 'value' })).toThrow();
|
|
231
|
-
expect(() => translator.translate({ $not: [{ field: 'value' }] } as any)).toThrow();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it('throws error for $not if empty', () => {
|
|
235
|
-
expect(() => translator.translate({ $not: {} })).toThrow();
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// Nested Objects and Fields
|
|
240
|
-
describe('nested objects and fields', () => {
|
|
241
|
-
it('handles nested objects', () => {
|
|
242
|
-
const filter: MongoDBVectorFilter = {
|
|
243
|
-
'user.profile.age': { $gt: 25 },
|
|
244
|
-
'user.status': 'active',
|
|
245
|
-
};
|
|
246
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it('handles deeply nested field paths', () => {
|
|
250
|
-
const filter: MongoDBVectorFilter = {
|
|
251
|
-
'user.profile.address.city': { $eq: 'New York' },
|
|
252
|
-
'deep.nested.field': { $gt: 100 },
|
|
253
|
-
};
|
|
254
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('preserves nested empty objects', () => {
|
|
258
|
-
const filter = {
|
|
259
|
-
status: 'active',
|
|
260
|
-
metadata: {},
|
|
261
|
-
user: {
|
|
262
|
-
profile: {},
|
|
263
|
-
settings: { theme: null },
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it('handles mix of operators and empty objects', () => {
|
|
270
|
-
const filter = {
|
|
271
|
-
tags: { $in: ['a', 'b'] },
|
|
272
|
-
metadata: {},
|
|
273
|
-
nested: {
|
|
274
|
-
field: { $eq: 'value' },
|
|
275
|
-
empty: {},
|
|
276
|
-
},
|
|
277
|
-
};
|
|
278
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('handles deeply nested operators', () => {
|
|
282
|
-
const filter = {
|
|
283
|
-
user: {
|
|
284
|
-
profile: {
|
|
285
|
-
preferences: {
|
|
286
|
-
theme: { $in: ['dark', 'light'] },
|
|
287
|
-
},
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
};
|
|
291
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
// Special Cases
|
|
296
|
-
describe('special cases', () => {
|
|
297
|
-
it('handles empty filters', () => {
|
|
298
|
-
expect(translator.translate({})).toEqual({});
|
|
299
|
-
expect(translator.translate(null)).toEqual(null);
|
|
300
|
-
expect(translator.translate(undefined)).toEqual(undefined);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it('normalizes dates', () => {
|
|
304
|
-
const date = new Date('2024-01-01');
|
|
305
|
-
const filter: MongoDBVectorFilter = { timestamp: { $gt: date } };
|
|
306
|
-
expect(translator.translate(filter)).toEqual({
|
|
307
|
-
timestamp: { $gt: date.toISOString() },
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
it('allows $not in field-level conditions', () => {
|
|
312
|
-
expect(() =>
|
|
313
|
-
translator.translate({
|
|
314
|
-
field1: { $not: { $eq: 'value1' } },
|
|
315
|
-
field2: { $not: { $in: ['value2', 'value3'] } },
|
|
316
|
-
field3: { $not: { $regex: 'pattern' } },
|
|
317
|
-
}),
|
|
318
|
-
).not.toThrow();
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
// Regex Support
|
|
323
|
-
describe('regex support', () => {
|
|
324
|
-
it('handles $regex operator', () => {
|
|
325
|
-
const filter: MongoDBVectorFilter = {
|
|
326
|
-
name: { $regex: '^test' },
|
|
327
|
-
};
|
|
328
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it('handles RegExp objects', () => {
|
|
332
|
-
const filter: MongoDBVectorFilter = {
|
|
333
|
-
name: /^test/i,
|
|
334
|
-
};
|
|
335
|
-
// RegExp objects should be preserved
|
|
336
|
-
expect(translator.translate(filter)).toEqual(filter);
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
describe('operator validation', () => {
|
|
341
|
-
it('ensures all supported operator filters are accepted', () => {
|
|
342
|
-
const supportedFilters: MongoDBVectorFilter[] = [
|
|
343
|
-
// Basic comparison operators
|
|
344
|
-
{ field: { $eq: 'value' } },
|
|
345
|
-
{ field: { $ne: 'value' } },
|
|
346
|
-
{ field: { $gt: 'value' } },
|
|
347
|
-
{ field: { $gte: 'value' } },
|
|
348
|
-
{ field: { $lt: 'value' } },
|
|
349
|
-
{ field: { $lte: 'value' } },
|
|
350
|
-
|
|
351
|
-
// Array operators
|
|
352
|
-
{ field: { $in: ['value'] } },
|
|
353
|
-
{ field: { $nin: ['value'] } },
|
|
354
|
-
{ field: { $all: ['value'] } },
|
|
355
|
-
{ field: { $elemMatch: { $gt: 5 } } },
|
|
356
|
-
|
|
357
|
-
// Existence
|
|
358
|
-
{ field: { $exists: true } },
|
|
359
|
-
|
|
360
|
-
// Logical operators
|
|
361
|
-
{ $and: [{ field1: 'value1' }, { field2: 'value2' }] },
|
|
362
|
-
{ $or: [{ field1: 'value1' }, { field2: 'value2' }] },
|
|
363
|
-
{ $nor: [{ field1: 'value1' }, { field2: 'value2' }] },
|
|
364
|
-
{ $not: { field: 'value' } },
|
|
365
|
-
|
|
366
|
-
// Nested logical operators
|
|
367
|
-
{ $or: [{ $and: [{ field1: 'value1' }] }, { $not: { field2: 'value2' } }] },
|
|
368
|
-
|
|
369
|
-
// Field-level $not
|
|
370
|
-
{ field: { $not: { $eq: 'value' } } },
|
|
371
|
-
{ field: { $not: { $in: ['value1', 'value2'] } } },
|
|
372
|
-
{ field: { $not: { $gt: 100 } } },
|
|
373
|
-
{ field: { $not: { $lt: 50 } } },
|
|
374
|
-
|
|
375
|
-
// Custom operators
|
|
376
|
-
{ field: { $size: 1 } },
|
|
377
|
-
{ field: { $regex: 'pattern' } },
|
|
378
|
-
];
|
|
379
|
-
|
|
380
|
-
supportedFilters.forEach(filter => {
|
|
381
|
-
expect(() => translator.translate(filter)).not.toThrow();
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it('throws on unsupported operators', () => {
|
|
386
|
-
expect(() => translator.translate({ field: { $unknown: 'value' } } as any)).toThrow(
|
|
387
|
-
'Unsupported operator: $unknown',
|
|
388
|
-
);
|
|
389
|
-
expect(() => translator.translate({ $unknown: [{ field: 'value' }] } as any)).toThrow(
|
|
390
|
-
'Unsupported operator: $unknown',
|
|
391
|
-
);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
it('throws error for non-logical operators at top level', () => {
|
|
395
|
-
const invalidFilters: any = [
|
|
396
|
-
{ $gt: 100 },
|
|
397
|
-
{ $in: ['value1', 'value2'] },
|
|
398
|
-
{ $exists: true },
|
|
399
|
-
{ $regex: 'pattern' },
|
|
400
|
-
];
|
|
401
|
-
|
|
402
|
-
invalidFilters.forEach(filter => {
|
|
403
|
-
expect(() => translator.translate(filter)).toThrow(/Invalid top-level operator/);
|
|
404
|
-
});
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
it('allows logical operators at top level', () => {
|
|
408
|
-
const validFilters = [
|
|
409
|
-
{ $and: [{ field: 'value' }] },
|
|
410
|
-
{ $or: [{ field: 'value' }] },
|
|
411
|
-
{ $nor: [{ field: 'value' }] },
|
|
412
|
-
{ $not: { field: 'value' } },
|
|
413
|
-
];
|
|
414
|
-
|
|
415
|
-
validFilters.forEach(filter => {
|
|
416
|
-
expect(() => translator.translate(filter)).not.toThrow();
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
});
|
package/src/vector/filter.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
2
|
-
import type {
|
|
3
|
-
VectorFilter,
|
|
4
|
-
OperatorSupport,
|
|
5
|
-
QueryOperator,
|
|
6
|
-
OperatorValueMap,
|
|
7
|
-
LogicalOperatorValueMap,
|
|
8
|
-
BlacklistedRootOperators,
|
|
9
|
-
VectorFieldValue,
|
|
10
|
-
} from '@mastra/core/vector/filter';
|
|
11
|
-
|
|
12
|
-
type MongoDBOperatorValueMap = Omit<OperatorValueMap, '$options'> & {
|
|
13
|
-
$size: number;
|
|
14
|
-
};
|
|
15
|
-
type MongoDBBlacklisted = BlacklistedRootOperators | '$size';
|
|
16
|
-
|
|
17
|
-
export type MongoDBVectorFilter = VectorFilter<
|
|
18
|
-
keyof MongoDBOperatorValueMap,
|
|
19
|
-
MongoDBOperatorValueMap,
|
|
20
|
-
LogicalOperatorValueMap,
|
|
21
|
-
MongoDBBlacklisted,
|
|
22
|
-
VectorFieldValue | RegExp
|
|
23
|
-
>;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Translator for MongoDB filter queries.
|
|
27
|
-
* Maintains MongoDB-compatible syntax while ensuring proper validation
|
|
28
|
-
* and normalization of values.
|
|
29
|
-
*/
|
|
30
|
-
export class MongoDBFilterTranslator extends BaseFilterTranslator<MongoDBVectorFilter> {
|
|
31
|
-
protected override getSupportedOperators(): OperatorSupport {
|
|
32
|
-
return {
|
|
33
|
-
...BaseFilterTranslator.DEFAULT_OPERATORS,
|
|
34
|
-
regex: ['$regex'],
|
|
35
|
-
custom: ['$size'],
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
translate(filter?: MongoDBVectorFilter): any {
|
|
40
|
-
if (this.isEmpty(filter)) return filter;
|
|
41
|
-
this.validateFilter(filter);
|
|
42
|
-
|
|
43
|
-
return this.translateNode(filter);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
private translateNode(node: MongoDBVectorFilter): any {
|
|
47
|
-
// Handle primitive values and arrays
|
|
48
|
-
if (this.isRegex(node)) {
|
|
49
|
-
return node; // Return regex values as-is
|
|
50
|
-
}
|
|
51
|
-
if (this.isPrimitive(node)) return node;
|
|
52
|
-
if (Array.isArray(node)) return node;
|
|
53
|
-
|
|
54
|
-
const entries = Object.entries(node as Record<string, any>);
|
|
55
|
-
const translatedEntries = entries.map(([key, value]) => {
|
|
56
|
-
// Handle operators
|
|
57
|
-
if (this.isOperator(key)) {
|
|
58
|
-
return [key, this.translateOperatorValue(key, value)];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Handle nested paths and objects
|
|
62
|
-
return [key, this.translateNode(value)];
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return Object.fromEntries(translatedEntries);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
private translateOperatorValue(operator: QueryOperator, value: any): any {
|
|
69
|
-
// Handle logical operators
|
|
70
|
-
if (this.isLogicalOperator(operator)) {
|
|
71
|
-
if (operator === '$not') {
|
|
72
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
73
|
-
throw new Error('$not operator requires an object');
|
|
74
|
-
}
|
|
75
|
-
if (this.isEmpty(value)) {
|
|
76
|
-
throw new Error('$not operator cannot be empty');
|
|
77
|
-
}
|
|
78
|
-
return this.translateNode(value);
|
|
79
|
-
} else {
|
|
80
|
-
if (!Array.isArray(value)) {
|
|
81
|
-
throw new Error(`Value for logical operator ${operator} must be an array`);
|
|
82
|
-
}
|
|
83
|
-
return value.map(item => this.translateNode(item));
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Handle basic and numeric operators
|
|
88
|
-
if (this.isBasicOperator(operator) || this.isNumericOperator(operator)) {
|
|
89
|
-
// Convert Date to ISO string
|
|
90
|
-
if (value instanceof Date) {
|
|
91
|
-
return value.toISOString();
|
|
92
|
-
}
|
|
93
|
-
return this.normalizeComparisonValue(value);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Handle $elemMatch operator - place this before array operators check
|
|
97
|
-
if (operator === '$elemMatch') {
|
|
98
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
99
|
-
throw new Error(`Value for $elemMatch operator must be an object`);
|
|
100
|
-
}
|
|
101
|
-
return this.translateNode(value);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Handle array operators
|
|
105
|
-
if (this.isArrayOperator(operator)) {
|
|
106
|
-
if (!Array.isArray(value)) {
|
|
107
|
-
throw new Error(`Value for array operator ${operator} must be an array`);
|
|
108
|
-
}
|
|
109
|
-
return this.normalizeArrayValues(value);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Handle element operators
|
|
113
|
-
if (this.isElementOperator(operator)) {
|
|
114
|
-
if (operator === '$exists' && typeof value !== 'boolean') {
|
|
115
|
-
throw new Error(`Value for $exists operator must be a boolean`);
|
|
116
|
-
}
|
|
117
|
-
return value;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Handle regex operators
|
|
121
|
-
if (this.isRegexOperator(operator)) {
|
|
122
|
-
if (!(value instanceof RegExp) && typeof value !== 'string') {
|
|
123
|
-
throw new Error(`Value for ${operator} operator must be a RegExp or string`);
|
|
124
|
-
}
|
|
125
|
-
return value;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Handle $size operator
|
|
129
|
-
if (operator === '$size') {
|
|
130
|
-
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
|
|
131
|
-
throw new Error(`Value for $size operator must be a non-negative integer`);
|
|
132
|
-
}
|
|
133
|
-
return value;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// If we get here, the operator is not supported
|
|
137
|
-
throw new Error(`Unsupported operator: ${operator}`);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
isEmpty(filter: any): boolean {
|
|
141
|
-
return filter === undefined || filter === null || (typeof filter === 'object' && Object.keys(filter).length === 0);
|
|
142
|
-
}
|
|
143
|
-
}
|