@mastra/mongodb 0.0.0-working-memory-per-user-20250620163010 → 0.0.0-zod-v4-compat-part-2-20250820135355

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +251 -3
  2. package/LICENSE.md +11 -42
  3. package/dist/index.cjs +1846 -505
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +5 -7
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +1815 -474
  8. package/dist/index.js.map +1 -0
  9. package/dist/storage/MongoDBConnector.d.ts +23 -0
  10. package/dist/storage/MongoDBConnector.d.ts.map +1 -0
  11. package/dist/storage/connectors/MongoDBConnector.d.ts +23 -0
  12. package/dist/storage/connectors/MongoDBConnector.d.ts.map +1 -0
  13. package/dist/storage/connectors/base.d.ts +6 -0
  14. package/dist/storage/connectors/base.d.ts.map +1 -0
  15. package/dist/storage/domains/legacy-evals/index.d.ts +18 -0
  16. package/dist/storage/domains/legacy-evals/index.d.ts.map +1 -0
  17. package/dist/storage/domains/memory/index.d.ts +80 -0
  18. package/dist/storage/domains/memory/index.d.ts.map +1 -0
  19. package/dist/storage/domains/operations/index.d.ts +38 -0
  20. package/dist/storage/domains/operations/index.d.ts.map +1 -0
  21. package/dist/storage/domains/scores/index.d.ts +41 -0
  22. package/dist/storage/domains/scores/index.d.ts.map +1 -0
  23. package/dist/storage/domains/traces/index.d.ts +18 -0
  24. package/dist/storage/domains/traces/index.d.ts.map +1 -0
  25. package/dist/storage/domains/utils.d.ts +8 -0
  26. package/dist/storage/domains/utils.d.ts.map +1 -0
  27. package/dist/storage/domains/workflows/index.d.ts +33 -0
  28. package/dist/storage/domains/workflows/index.d.ts.map +1 -0
  29. package/dist/storage/index.d.ts +178 -0
  30. package/dist/storage/index.d.ts.map +1 -0
  31. package/dist/storage/types.d.ts +11 -0
  32. package/dist/storage/types.d.ts.map +1 -0
  33. package/dist/vector/filter.d.ts +21 -0
  34. package/dist/vector/filter.d.ts.map +1 -0
  35. package/dist/vector/index.d.ts +78 -0
  36. package/dist/vector/index.d.ts.map +1 -0
  37. package/dist/vector/prompt.d.ts +6 -0
  38. package/dist/vector/prompt.d.ts.map +1 -0
  39. package/docker-compose.yaml +1 -1
  40. package/package.json +10 -10
  41. package/src/index.ts +1 -0
  42. package/src/storage/MongoDBConnector.ts +93 -0
  43. package/src/storage/connectors/MongoDBConnector.ts +93 -0
  44. package/src/storage/connectors/base.ts +7 -0
  45. package/src/storage/domains/legacy-evals/index.ts +193 -0
  46. package/src/storage/domains/memory/index.ts +741 -0
  47. package/src/storage/domains/operations/index.ts +155 -0
  48. package/src/storage/domains/scores/index.ts +379 -0
  49. package/src/storage/domains/traces/index.ts +142 -0
  50. package/src/storage/domains/utils.ts +43 -0
  51. package/src/storage/domains/workflows/index.ts +196 -0
  52. package/src/storage/index.test.ts +27 -989
  53. package/src/storage/index.ts +241 -605
  54. package/src/storage/types.ts +14 -0
  55. package/src/vector/filter.test.ts +40 -30
  56. package/src/vector/filter.ts +25 -4
  57. package/src/vector/index.test.ts +48 -3
  58. package/src/vector/index.ts +301 -131
  59. package/tsconfig.build.json +9 -0
  60. package/tsconfig.json +1 -1
  61. package/tsup.config.ts +22 -0
  62. package/dist/_tsup-dts-rollup.d.cts +0 -274
  63. package/dist/_tsup-dts-rollup.d.ts +0 -274
  64. package/dist/index.d.cts +0 -7
@@ -0,0 +1,14 @@
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,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 as any)).toEqual(null);
299
- expect(translator.translate(undefined as any)).toEqual(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('Unsupported operator: $unknown');
386
- expect(() => translator.translate({ $unknown: [{ field: 'value' }] })).toThrow('Unsupported operator: $unknown');
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 = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $exists: true }, { $regex: 'pattern' }];
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/);
@@ -1,12 +1,33 @@
1
1
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
2
- import type { FieldCondition, VectorFilter, OperatorSupport, QueryOperator } 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
+ >;
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?: VectorFilter): any {
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: VectorFilter | FieldCondition): any {
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
@@ -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
 
@@ -94,6 +93,7 @@ describe('MongoDBVector Integration Tests', () => {
94
93
  let vectorDB: MongoDBVector;
95
94
  const testIndexName = 'my_vectors';
96
95
  const testIndexName2 = 'my_vectors_2';
96
+ const emptyIndexName = 'empty-index';
97
97
 
98
98
  beforeAll(async () => {
99
99
  vectorDB = new MongoDBVector({ uri, dbName });
@@ -115,6 +115,7 @@ describe('MongoDBVector Integration Tests', () => {
115
115
 
116
116
  await createIndexAndWait(vectorDB, testIndexName, 4, 'cosine');
117
117
  await createIndexAndWait(vectorDB, testIndexName2, 4, 'cosine');
118
+ await createIndexAndWait(vectorDB, emptyIndexName, 4, 'cosine');
118
119
  }, 500000);
119
120
 
120
121
  afterAll(async () => {
@@ -128,6 +129,11 @@ describe('MongoDBVector Integration Tests', () => {
128
129
  } catch (error) {
129
130
  console.error('Failed to delete test collection:', error);
130
131
  }
132
+ try {
133
+ await vectorDB.deleteIndex({ indexName: emptyIndexName });
134
+ } catch (error) {
135
+ console.error('Failed to delete test collection:', error);
136
+ }
131
137
  await vectorDB.disconnect();
132
138
  });
133
139
 
@@ -264,7 +270,10 @@ describe('MongoDBVector Integration Tests', () => {
264
270
  beforeAll(async () => {
265
271
  // Ensure testIndexName2 has at least one document
266
272
  const testVector = [1, 0, 0, 0];
267
- const testMetadata = { label: 'test_filter_validation' };
273
+ const testMetadata = {
274
+ label: 'test_filter_validation',
275
+ timestamp: new Date('2024-01-01T00:00:00Z'),
276
+ };
268
277
 
269
278
  // First check if there are already documents
270
279
  const existingResults = await vectorDB.query({
@@ -329,7 +338,7 @@ describe('MongoDBVector Integration Tests', () => {
329
338
  const results = await retryQuery({
330
339
  indexName: testIndexName2,
331
340
  queryVector: [1, 0, 0, 0],
332
- filter: null as unknown as VectorFilter,
341
+ filter: null,
333
342
  });
334
343
  const results2 = await retryQuery({
335
344
  indexName: testIndexName2,
@@ -339,6 +348,18 @@ describe('MongoDBVector Integration Tests', () => {
339
348
  expect(results.length).toBeGreaterThan(0);
340
349
  });
341
350
 
351
+ it('handles filters with multiple properties', async () => {
352
+ const results = await retryQuery({
353
+ indexName: testIndexName2,
354
+ queryVector: [1, 0, 0, 0],
355
+ filter: {
356
+ 'metadata.label': 'test_filter_validation',
357
+ 'metadata.timestamp': { $gt: new Date('2023-01-01T00:00:00Z') },
358
+ },
359
+ });
360
+ expect(results.length).toBeGreaterThan(0);
361
+ });
362
+
342
363
  it('normalizes date values in filter using filter.ts', async () => {
343
364
  const vector = [1, 0, 0, 0];
344
365
  const timestampDate = new Date('2024-01-01T00:00:00Z');
@@ -468,5 +489,29 @@ describe('MongoDBVector Integration Tests', () => {
468
489
  const invalidVector = [1, 2, 3]; // 3D vector for 4D index
469
490
  await expect(vectorDB.upsert({ indexName: testIndexName, vectors: [invalidVector] })).rejects.toThrow();
470
491
  });
492
+ it('should return empty results and not throw when semantic search filter matches zero documents', async () => {
493
+ // Use a valid embedding vector matching your test index dimension
494
+ const testEmbedding = [0.1, 0.2, 0.3, 0.4]; // Adjust dimension as needed
495
+
496
+ // Should not throw, should return an empty array
497
+ let error: unknown = null;
498
+ let results: any[] = [];
499
+ try {
500
+ results = await vectorDB.query({
501
+ indexName: emptyIndexName,
502
+ queryVector: testEmbedding,
503
+ topK: 2,
504
+ filter: {
505
+ 'metadata.label': 'test_filter_validation',
506
+ },
507
+ });
508
+ } catch (e) {
509
+ error = e;
510
+ }
511
+
512
+ expect(error).toBeNull();
513
+ expect(Array.isArray(results)).toBe(true);
514
+ expect(results.length).toBe(0);
515
+ });
471
516
  });
472
517
  });