@mastra/mongodb 0.11.1-alpha.0 → 0.11.1-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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +28 -0
- package/dist/_tsup-dts-rollup.d.cts +17 -7
- package/dist/_tsup-dts-rollup.d.ts +17 -7
- package/dist/index.cjs +560 -226
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +539 -205
- package/package.json +4 -4
- package/src/storage/index.test.ts +239 -4
- package/src/storage/index.ts +295 -106
- package/src/vector/filter.test.ts +40 -30
- package/src/vector/filter.ts +25 -4
- package/src/vector/index.test.ts +1 -2
- package/src/vector/index.ts +275 -130
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
2
|
|
|
3
|
+
import type { MongoDBVectorFilter } from './filter';
|
|
3
4
|
import { MongoDBFilterTranslator } from './filter';
|
|
4
5
|
|
|
5
6
|
describe('MongoDBFilterTranslator', () => {
|
|
@@ -12,12 +13,12 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
12
13
|
// Basic Filter Operations
|
|
13
14
|
describe('basic operations', () => {
|
|
14
15
|
it('handles simple equality', () => {
|
|
15
|
-
const filter = { field: 'value' };
|
|
16
|
+
const filter: MongoDBVectorFilter = { field: 'value' };
|
|
16
17
|
expect(translator.translate(filter)).toEqual(filter);
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
it('handles comparison operators', () => {
|
|
20
|
-
const filter = {
|
|
21
|
+
const filter: MongoDBVectorFilter = {
|
|
21
22
|
age: { $gt: 25 },
|
|
22
23
|
score: { $lte: 100 },
|
|
23
24
|
};
|
|
@@ -25,7 +26,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
it('handles valid multiple operators on same field', () => {
|
|
28
|
-
const filter = {
|
|
29
|
+
const filter: MongoDBVectorFilter = {
|
|
29
30
|
price: { $gt: 100, $lt: 200 },
|
|
30
31
|
quantity: { $gte: 10, $lte: 20 },
|
|
31
32
|
};
|
|
@@ -33,7 +34,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
it('handles null values correctly', () => {
|
|
36
|
-
const filter = {
|
|
37
|
+
const filter: MongoDBVectorFilter = {
|
|
37
38
|
field: null,
|
|
38
39
|
other: { $eq: null },
|
|
39
40
|
};
|
|
@@ -41,7 +42,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
it('handles boolean values correctly', () => {
|
|
44
|
-
const filter = {
|
|
45
|
+
const filter: MongoDBVectorFilter = {
|
|
45
46
|
active: true,
|
|
46
47
|
deleted: false,
|
|
47
48
|
status: { $eq: true },
|
|
@@ -53,7 +54,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
53
54
|
// Array Operations
|
|
54
55
|
describe('array operations', () => {
|
|
55
56
|
it('handles array operators', () => {
|
|
56
|
-
const filter = {
|
|
57
|
+
const filter: MongoDBVectorFilter = {
|
|
57
58
|
tags: { $all: ['tag1', 'tag2'] },
|
|
58
59
|
categories: { $in: ['A', 'B'] },
|
|
59
60
|
items: { $nin: ['item1', 'item2'] },
|
|
@@ -63,7 +64,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
63
64
|
});
|
|
64
65
|
|
|
65
66
|
it('handles empty array values', () => {
|
|
66
|
-
const filter = {
|
|
67
|
+
const filter: MongoDBVectorFilter = {
|
|
67
68
|
tags: { $in: [] },
|
|
68
69
|
categories: { $all: [] },
|
|
69
70
|
};
|
|
@@ -71,14 +72,14 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
71
72
|
});
|
|
72
73
|
|
|
73
74
|
it('handles nested array operators', () => {
|
|
74
|
-
const filter = {
|
|
75
|
+
const filter: MongoDBVectorFilter = {
|
|
75
76
|
$and: [{ tags: { $all: ['tag1', 'tag2'] } }, { 'nested.array': { $in: [1, 2, 3] } }],
|
|
76
77
|
};
|
|
77
78
|
expect(translator.translate(filter)).toEqual(filter);
|
|
78
79
|
});
|
|
79
80
|
|
|
80
81
|
it('handles $size operator', () => {
|
|
81
|
-
const filter = {
|
|
82
|
+
const filter: MongoDBVectorFilter = {
|
|
82
83
|
tags: { $size: 3 },
|
|
83
84
|
};
|
|
84
85
|
expect(translator.translate(filter)).toEqual(filter);
|
|
@@ -88,14 +89,14 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
88
89
|
// Logical Operators
|
|
89
90
|
describe('logical operators', () => {
|
|
90
91
|
it('handles logical operators', () => {
|
|
91
|
-
const filter = {
|
|
92
|
+
const filter: MongoDBVectorFilter = {
|
|
92
93
|
$or: [{ status: 'active' }, { age: { $gt: 25 } }],
|
|
93
94
|
};
|
|
94
95
|
expect(translator.translate(filter)).toEqual(filter);
|
|
95
96
|
});
|
|
96
97
|
|
|
97
98
|
it('handles $not operator', () => {
|
|
98
|
-
const filter = {
|
|
99
|
+
const filter: MongoDBVectorFilter = {
|
|
99
100
|
field: { $not: { $eq: 'value' } },
|
|
100
101
|
$not: { field: 'value' },
|
|
101
102
|
};
|
|
@@ -103,14 +104,14 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
103
104
|
});
|
|
104
105
|
|
|
105
106
|
it('handles $nor operator', () => {
|
|
106
|
-
const filter = {
|
|
107
|
+
const filter: MongoDBVectorFilter = {
|
|
107
108
|
$nor: [{ status: 'deleted' }, { active: false }],
|
|
108
109
|
};
|
|
109
110
|
expect(translator.translate(filter)).toEqual(filter);
|
|
110
111
|
});
|
|
111
112
|
|
|
112
113
|
it('handles nested logical operators', () => {
|
|
113
|
-
const filter = {
|
|
114
|
+
const filter: MongoDBVectorFilter = {
|
|
114
115
|
$and: [
|
|
115
116
|
{ status: 'active' },
|
|
116
117
|
{ $or: [{ category: { $in: ['A', 'B'] } }, { $and: [{ price: { $gt: 100 } }, { stock: { $lt: 50 } }] }] },
|
|
@@ -120,7 +121,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
120
121
|
});
|
|
121
122
|
|
|
122
123
|
it('handles empty conditions in logical operators', () => {
|
|
123
|
-
const filter = {
|
|
124
|
+
const filter: MongoDBVectorFilter = {
|
|
124
125
|
$and: [],
|
|
125
126
|
$or: [{}],
|
|
126
127
|
field: 'value',
|
|
@@ -175,7 +176,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
175
176
|
expect(() =>
|
|
176
177
|
translator.translate({
|
|
177
178
|
$or: [{ $in: ['value1', 'value2'] }],
|
|
178
|
-
}),
|
|
179
|
+
} as any),
|
|
179
180
|
).toThrow(/Logical operators must contain field conditions/);
|
|
180
181
|
});
|
|
181
182
|
|
|
@@ -187,7 +188,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
187
188
|
$or: [{ subfield: 'value1' }, { subfield: 'value2' }],
|
|
188
189
|
},
|
|
189
190
|
},
|
|
190
|
-
}),
|
|
191
|
+
} as any),
|
|
191
192
|
).toThrow();
|
|
192
193
|
|
|
193
194
|
expect(() =>
|
|
@@ -196,7 +197,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
196
197
|
$in: [
|
|
197
198
|
{
|
|
198
199
|
$and: [{ subfield: 'value1' }, { subfield: 'value2' }],
|
|
199
|
-
},
|
|
200
|
+
} as any,
|
|
200
201
|
],
|
|
201
202
|
},
|
|
202
203
|
}),
|
|
@@ -211,7 +212,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
211
212
|
$or: [{ subfield: 'value1' }, { subfield: 'value2' }],
|
|
212
213
|
},
|
|
213
214
|
},
|
|
214
|
-
}),
|
|
215
|
+
} as any),
|
|
215
216
|
).toThrow();
|
|
216
217
|
|
|
217
218
|
expect(() =>
|
|
@@ -227,7 +228,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
227
228
|
|
|
228
229
|
it('throws error for $not if not an object', () => {
|
|
229
230
|
expect(() => translator.translate({ $not: 'value' })).toThrow();
|
|
230
|
-
expect(() => translator.translate({ $not: [{ field: 'value' }] })).toThrow();
|
|
231
|
+
expect(() => translator.translate({ $not: [{ field: 'value' }] } as any)).toThrow();
|
|
231
232
|
});
|
|
232
233
|
|
|
233
234
|
it('throws error for $not if empty', () => {
|
|
@@ -238,7 +239,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
238
239
|
// Nested Objects and Fields
|
|
239
240
|
describe('nested objects and fields', () => {
|
|
240
241
|
it('handles nested objects', () => {
|
|
241
|
-
const filter = {
|
|
242
|
+
const filter: MongoDBVectorFilter = {
|
|
242
243
|
'user.profile.age': { $gt: 25 },
|
|
243
244
|
'user.status': 'active',
|
|
244
245
|
};
|
|
@@ -246,7 +247,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
246
247
|
});
|
|
247
248
|
|
|
248
249
|
it('handles deeply nested field paths', () => {
|
|
249
|
-
const filter = {
|
|
250
|
+
const filter: MongoDBVectorFilter = {
|
|
250
251
|
'user.profile.address.city': { $eq: 'New York' },
|
|
251
252
|
'deep.nested.field': { $gt: 100 },
|
|
252
253
|
};
|
|
@@ -295,13 +296,13 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
295
296
|
describe('special cases', () => {
|
|
296
297
|
it('handles empty filters', () => {
|
|
297
298
|
expect(translator.translate({})).toEqual({});
|
|
298
|
-
expect(translator.translate(null
|
|
299
|
-
expect(translator.translate(undefined
|
|
299
|
+
expect(translator.translate(null)).toEqual(null);
|
|
300
|
+
expect(translator.translate(undefined)).toEqual(undefined);
|
|
300
301
|
});
|
|
301
302
|
|
|
302
303
|
it('normalizes dates', () => {
|
|
303
304
|
const date = new Date('2024-01-01');
|
|
304
|
-
const filter = { timestamp: { $gt: date } };
|
|
305
|
+
const filter: MongoDBVectorFilter = { timestamp: { $gt: date } };
|
|
305
306
|
expect(translator.translate(filter)).toEqual({
|
|
306
307
|
timestamp: { $gt: date.toISOString() },
|
|
307
308
|
});
|
|
@@ -321,14 +322,14 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
321
322
|
// Regex Support
|
|
322
323
|
describe('regex support', () => {
|
|
323
324
|
it('handles $regex operator', () => {
|
|
324
|
-
const filter = {
|
|
325
|
+
const filter: MongoDBVectorFilter = {
|
|
325
326
|
name: { $regex: '^test' },
|
|
326
327
|
};
|
|
327
328
|
expect(translator.translate(filter)).toEqual(filter);
|
|
328
329
|
});
|
|
329
330
|
|
|
330
331
|
it('handles RegExp objects', () => {
|
|
331
|
-
const filter = {
|
|
332
|
+
const filter: MongoDBVectorFilter = {
|
|
332
333
|
name: /^test/i,
|
|
333
334
|
};
|
|
334
335
|
// RegExp objects should be preserved
|
|
@@ -338,7 +339,7 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
338
339
|
|
|
339
340
|
describe('operator validation', () => {
|
|
340
341
|
it('ensures all supported operator filters are accepted', () => {
|
|
341
|
-
const supportedFilters = [
|
|
342
|
+
const supportedFilters: MongoDBVectorFilter[] = [
|
|
342
343
|
// Basic comparison operators
|
|
343
344
|
{ field: { $eq: 'value' } },
|
|
344
345
|
{ field: { $ne: 'value' } },
|
|
@@ -382,12 +383,21 @@ describe('MongoDBFilterTranslator', () => {
|
|
|
382
383
|
});
|
|
383
384
|
|
|
384
385
|
it('throws on unsupported operators', () => {
|
|
385
|
-
expect(() => translator.translate({ field: { $unknown: 'value' } })).toThrow(
|
|
386
|
-
|
|
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
|
+
);
|
|
387
392
|
});
|
|
388
393
|
|
|
389
394
|
it('throws error for non-logical operators at top level', () => {
|
|
390
|
-
const invalidFilters
|
|
395
|
+
const invalidFilters: any = [
|
|
396
|
+
{ $gt: 100 },
|
|
397
|
+
{ $in: ['value1', 'value2'] },
|
|
398
|
+
{ $exists: true },
|
|
399
|
+
{ $regex: 'pattern' },
|
|
400
|
+
];
|
|
391
401
|
|
|
392
402
|
invalidFilters.forEach(filter => {
|
|
393
403
|
expect(() => translator.translate(filter)).toThrow(/Invalid top-level operator/);
|
package/src/vector/filter.ts
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
2
|
-
import type {
|
|
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
|
+
>;
|
|
3
24
|
|
|
4
25
|
/**
|
|
5
26
|
* Translator for MongoDB filter queries.
|
|
6
27
|
* Maintains MongoDB-compatible syntax while ensuring proper validation
|
|
7
28
|
* and normalization of values.
|
|
8
29
|
*/
|
|
9
|
-
export class MongoDBFilterTranslator extends BaseFilterTranslator {
|
|
30
|
+
export class MongoDBFilterTranslator extends BaseFilterTranslator<MongoDBVectorFilter> {
|
|
10
31
|
protected override getSupportedOperators(): OperatorSupport {
|
|
11
32
|
return {
|
|
12
33
|
...BaseFilterTranslator.DEFAULT_OPERATORS,
|
|
@@ -15,14 +36,14 @@ export class MongoDBFilterTranslator extends BaseFilterTranslator {
|
|
|
15
36
|
};
|
|
16
37
|
}
|
|
17
38
|
|
|
18
|
-
translate(filter?:
|
|
39
|
+
translate(filter?: MongoDBVectorFilter): any {
|
|
19
40
|
if (this.isEmpty(filter)) return filter;
|
|
20
41
|
this.validateFilter(filter);
|
|
21
42
|
|
|
22
43
|
return this.translateNode(filter);
|
|
23
44
|
}
|
|
24
45
|
|
|
25
|
-
private translateNode(node:
|
|
46
|
+
private translateNode(node: MongoDBVectorFilter): any {
|
|
26
47
|
// Handle primitive values and arrays
|
|
27
48
|
if (this.isRegex(node)) {
|
|
28
49
|
return node; // Return regex values as-is
|
package/src/vector/index.test.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { VectorFilter } from '@mastra/core/vector/filter';
|
|
2
1
|
import { vi, describe, it, expect, beforeAll, afterAll, test } from 'vitest';
|
|
3
2
|
import { MongoDBVector } from './';
|
|
4
3
|
|
|
@@ -329,7 +328,7 @@ describe('MongoDBVector Integration Tests', () => {
|
|
|
329
328
|
const results = await retryQuery({
|
|
330
329
|
indexName: testIndexName2,
|
|
331
330
|
queryVector: [1, 0, 0, 0],
|
|
332
|
-
filter: null
|
|
331
|
+
filter: null,
|
|
333
332
|
});
|
|
334
333
|
const results2 = await retryQuery({
|
|
335
334
|
indexName: testIndexName2,
|