@mastra/mongodb 0.13.5 → 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.
@@ -1,651 +0,0 @@
1
- import { createVectorTestSuite } from '@internal/storage-test-utils';
2
- import { vi, describe, it, expect, beforeAll, afterAll, test } from 'vitest';
3
- import { MongoDBVector } from './';
4
-
5
- // Give tests enough time to complete database operations
6
- vi.setConfig({ testTimeout: 300000, hookTimeout: 300000 });
7
-
8
- // Concrete MongoDB configuration values – adjust these for your environment
9
- const uri =
10
- 'mongodb://mongodb:mongodb@localhost:27018/?authSource=admin&directConnection=true&serverSelectionTimeoutMS=2000';
11
- const dbName = 'vector_db';
12
-
13
- async function waitForAtlasSearchReady(
14
- vectorDB: MongoDBVector,
15
- indexName: string = 'dummy_vector_index',
16
- dimension: number = 1,
17
- metric: 'cosine' | 'euclidean' | 'dotproduct' = 'cosine',
18
- timeout: number = 300000,
19
- interval: number = 5000,
20
- ) {
21
- const start = Date.now();
22
- let lastError: any = null;
23
- let attempt = 0;
24
- while (Date.now() - start < timeout) {
25
- attempt++;
26
- try {
27
- await vectorDB.createIndex({ indexName, dimension, metric });
28
- // If it succeeds, we're ready
29
- console.log(`[waitForAtlasSearchReady] Atlas Search is ready! (attempt ${attempt})`);
30
- return;
31
- } catch (e: any) {
32
- lastError = e;
33
- console.log(`[waitForAtlasSearchReady] Not ready yet (attempt ${attempt}): ${e.message}`);
34
- await new Promise(res => setTimeout(res, interval));
35
- }
36
- }
37
- throw new Error(
38
- 'Atlas Search did not become ready in time. Last error: ' + (lastError ? lastError.message : 'unknown'),
39
- );
40
- }
41
- // Helper function to wait for a condition with timeout (similar to mdb_toolkit)
42
- async function waitForCondition(
43
- condition: () => Promise<boolean>,
44
- timeout: number = 10000,
45
- interval: number = 1000,
46
- ): Promise<boolean> {
47
- const startTime = Date.now();
48
- while (Date.now() - startTime < timeout) {
49
- if (await condition()) return true;
50
- await new Promise(resolve => setTimeout(resolve, interval));
51
- }
52
- return false;
53
- }
54
-
55
- // Create index and wait until the search index (named `${indexName}_vector_index`) is READY
56
- async function createIndexAndWait(
57
- vectorDB: MongoDBVector,
58
- indexName: string,
59
- dimension: number,
60
- metric: 'cosine' | 'euclidean' | 'dotproduct',
61
- ) {
62
- await vectorDB.createIndex({ indexName, dimension, metric });
63
- await vectorDB.waitForIndexReady({ indexName });
64
- const created = await waitForCondition(
65
- async () => {
66
- const cols = await vectorDB.listIndexes();
67
- return cols.includes(indexName);
68
- },
69
- 30000,
70
- 2000,
71
- );
72
- if (!created) throw new Error('Timed out waiting for collection to be created');
73
- }
74
-
75
- // Delete index (collection) and wait until it is removed
76
- async function deleteIndexAndWait(vectorDB: MongoDBVector, indexName: string) {
77
- try {
78
- await vectorDB.deleteIndex({ indexName });
79
- const deleted = await waitForCondition(
80
- async () => {
81
- const cols = await vectorDB.listIndexes();
82
- return !cols.includes(indexName);
83
- },
84
- 30000,
85
- 2000,
86
- );
87
- if (!deleted) throw new Error('Timed out waiting for collection to be deleted');
88
- } catch (error) {
89
- console.error(`Error deleting index ${indexName}:`, error);
90
- }
91
- }
92
-
93
- describe('MongoDBVector Integration Tests', () => {
94
- let vectorDB: MongoDBVector;
95
- const testIndexName = 'my_vectors';
96
- const testIndexName2 = 'my_vectors_2';
97
- const emptyIndexName = 'empty-index';
98
-
99
- beforeAll(async () => {
100
- vectorDB = new MongoDBVector({ uri, dbName });
101
- await vectorDB.connect();
102
-
103
- // Wait for Atlas Search to be ready
104
- await waitForAtlasSearchReady(vectorDB);
105
-
106
- // Cleanup any existing collections
107
- try {
108
- const cols = await vectorDB.listIndexes();
109
- await Promise.all(cols.map(c => vectorDB.deleteIndex({ indexName: c })));
110
- const deleted = await waitForCondition(async () => (await vectorDB.listIndexes()).length === 0, 30000, 2000);
111
- if (!deleted) throw new Error('Timed out waiting for collections to be deleted');
112
- } catch (error) {
113
- console.error('Failed to delete test collections:', error);
114
- throw error;
115
- }
116
-
117
- await createIndexAndWait(vectorDB, testIndexName, 4, 'cosine');
118
- await createIndexAndWait(vectorDB, testIndexName2, 4, 'cosine');
119
- await createIndexAndWait(vectorDB, emptyIndexName, 4, 'cosine');
120
- }, 500000);
121
-
122
- afterAll(async () => {
123
- try {
124
- await vectorDB.deleteIndex({ indexName: testIndexName });
125
- } catch (error) {
126
- console.error('Failed to delete test collection:', error);
127
- }
128
- try {
129
- await vectorDB.deleteIndex({ indexName: testIndexName2 });
130
- } catch (error) {
131
- console.error('Failed to delete test collection:', error);
132
- }
133
- try {
134
- await vectorDB.deleteIndex({ indexName: emptyIndexName });
135
- } catch (error) {
136
- console.error('Failed to delete test collection:', error);
137
- }
138
- await vectorDB.disconnect();
139
- });
140
-
141
- test('full vector database workflow', async () => {
142
- // Verify collection exists
143
- const cols = await vectorDB.listIndexes();
144
- expect(cols).toContain(testIndexName);
145
-
146
- // Check stats (should be zero docs initially)
147
- const initialStats = await vectorDB.describeIndex({ indexName: testIndexName });
148
- expect(initialStats).toEqual({ dimension: 4, metric: 'cosine', count: 0 });
149
-
150
- // Upsert 4 vectors with metadata
151
- const vectors = [
152
- [1, 0, 0, 0],
153
- [0, 1, 0, 0],
154
- [0, 0, 1, 0],
155
- [0, 0, 0, 1],
156
- ];
157
- const metadata = [{ label: 'vector1' }, { label: 'vector2' }, { label: 'vector3' }, { label: 'vector4' }];
158
- const ids = await vectorDB.upsert({ indexName: testIndexName, vectors, metadata });
159
- expect(ids).toHaveLength(4);
160
-
161
- // Wait for the document count to update (increased delay to 5000ms)
162
- await new Promise(resolve => setTimeout(resolve, 5000));
163
- const updatedStats = await vectorDB.describeIndex({ indexName: testIndexName });
164
- expect(updatedStats.count).toEqual(4);
165
-
166
- // Query for similar vectors (delay again to allow for index update)
167
- await new Promise(resolve => setTimeout(resolve, 5000));
168
- const queryVector = [1, 0, 0, 0];
169
- const results = await vectorDB.query({ indexName: testIndexName, queryVector, topK: 2 });
170
- expect(results).toHaveLength(2);
171
- expect(results[0]?.metadata).toEqual({ label: 'vector1' });
172
- expect(results[0]?.score).toBeCloseTo(1, 4);
173
-
174
- // Query using a filter via filter.ts translator
175
- const filteredResults = await vectorDB.query({
176
- indexName: testIndexName,
177
- queryVector,
178
- topK: 4, // Increased from 2 to 4 to ensure all vectors are considered before filtering
179
- filter: { 'metadata.label': 'vector2' },
180
- });
181
- expect(filteredResults).toHaveLength(1);
182
- expect(filteredResults[0]?.metadata).toEqual({ label: 'vector2' });
183
-
184
- // Final stats should show > 0 documents
185
- const finalStats = await vectorDB.describeIndex({ indexName: testIndexName });
186
- expect(finalStats.count).toBeGreaterThan(0);
187
- });
188
-
189
- test('gets vector results back from query with vector included', async () => {
190
- // Delay to allow index update after any writes
191
- await new Promise(resolve => setTimeout(resolve, 5000));
192
- const queryVector = [1, 0, 0, 0];
193
- const results = await vectorDB.query({
194
- indexName: testIndexName,
195
- queryVector,
196
- topK: 2,
197
- includeVector: true,
198
- });
199
- expect(results).toHaveLength(2);
200
- expect(results[0]?.metadata).toEqual({ label: 'vector1' });
201
- expect(results[0]?.score).toBeCloseTo(1, 4);
202
- expect(results[0]?.vector).toBeDefined();
203
- });
204
-
205
- test('handles different vector dimensions', async () => {
206
- const highDimIndexName = 'high_dim_test_' + Date.now();
207
- try {
208
- await createIndexAndWait(vectorDB, highDimIndexName, 1536, 'cosine');
209
- const vectors = [
210
- Array(1536)
211
- .fill(0)
212
- .map((_, i) => i % 2),
213
- Array(1536)
214
- .fill(0)
215
- .map((_, i) => (i + 1) % 2),
216
- ];
217
- const metadata = [{ label: 'even' }, { label: 'odd' }];
218
- const ids = await vectorDB.upsert({ indexName: highDimIndexName, vectors, metadata });
219
- expect(ids).toHaveLength(2);
220
- await new Promise(resolve => setTimeout(resolve, 5000));
221
- const queryVector = Array(1536)
222
- .fill(0)
223
- .map((_, i) => i % 2);
224
- const results = await vectorDB.query({ indexName: highDimIndexName, queryVector, topK: 2 });
225
- expect(results).toHaveLength(2);
226
- expect(results[0]?.metadata).toEqual({ label: 'even' });
227
- expect(results[0]?.score).toBeCloseTo(1, 4);
228
- } finally {
229
- await deleteIndexAndWait(vectorDB, highDimIndexName);
230
- }
231
- });
232
-
233
- test('handles different distance metrics', async () => {
234
- const metrics: Array<'cosine' | 'euclidean' | 'dotproduct'> = ['cosine', 'euclidean', 'dotproduct'];
235
- for (const metric of metrics) {
236
- const metricIndexName = `metrictest_${metric}_${Date.now()}`;
237
- try {
238
- await createIndexAndWait(vectorDB, metricIndexName, 4, metric);
239
- const vectors = [
240
- [1, 0, 0, 0],
241
- [0.7071, 0.7071, 0, 0],
242
- ];
243
- await vectorDB.upsert({ indexName: metricIndexName, vectors });
244
- await new Promise(resolve => setTimeout(resolve, 5000));
245
- const results = await vectorDB.query({ indexName: metricIndexName, queryVector: [1, 0, 0, 0], topK: 2 });
246
- expect(results).toHaveLength(2);
247
- expect(results[0]?.score).toBeGreaterThan(results[1]?.score ?? 0);
248
- } finally {
249
- await deleteIndexAndWait(vectorDB, metricIndexName);
250
- }
251
- }
252
- }, 500000);
253
-
254
- describe('Filter Validation in Queries', () => {
255
- // Helper function to retry queries with delay
256
- async function retryQuery(params: any, maxRetries = 2) {
257
- let results = await vectorDB.query(params);
258
- let retryCount = 0;
259
-
260
- // If no results, retry a few times with delay
261
- while (results.length === 0 && retryCount < maxRetries) {
262
- console.log(`No results found, retrying (${retryCount + 1}/${maxRetries})...`);
263
- await new Promise(resolve => setTimeout(resolve, 5000));
264
- results = await vectorDB.query(params);
265
- retryCount++;
266
- }
267
-
268
- return results;
269
- }
270
-
271
- beforeAll(async () => {
272
- // Ensure testIndexName2 has at least one document
273
- const testVector = [1, 0, 0, 0];
274
- const testMetadata = {
275
- label: 'test_filter_validation',
276
- timestamp: new Date('2024-01-01T00:00:00Z'),
277
- };
278
-
279
- // First check if there are already documents
280
- const existingResults = await vectorDB.query({
281
- indexName: testIndexName2,
282
- queryVector: testVector,
283
- topK: 1,
284
- });
285
-
286
- // If no documents exist, insert one
287
- if (existingResults.length === 0) {
288
- await vectorDB.upsert({
289
- indexName: testIndexName2,
290
- vectors: [testVector],
291
- metadata: [testMetadata],
292
- });
293
-
294
- // Wait for the document to be indexed
295
- await new Promise(resolve => setTimeout(resolve, 5000));
296
-
297
- // Verify the document is indexed
298
- const verifyResults = await retryQuery({
299
- indexName: testIndexName2,
300
- queryVector: testVector,
301
- topK: 1,
302
- });
303
-
304
- if (verifyResults.length === 0) {
305
- console.warn('Warning: Could not verify document was indexed in testIndexName2');
306
- }
307
- }
308
- }, 30000);
309
-
310
- it('handles undefined filter', async () => {
311
- const results1 = await retryQuery({
312
- indexName: testIndexName2,
313
- queryVector: [1, 0, 0, 0],
314
- filter: undefined,
315
- });
316
- const results2 = await retryQuery({
317
- indexName: testIndexName2,
318
- queryVector: [1, 0, 0, 0],
319
- });
320
- expect(results1).toEqual(results2);
321
- expect(results1.length).toBeGreaterThan(0);
322
- });
323
-
324
- it('handles empty object filter', async () => {
325
- const results = await retryQuery({
326
- indexName: testIndexName2,
327
- queryVector: [1, 0, 0, 0],
328
- filter: {},
329
- });
330
- const results2 = await retryQuery({
331
- indexName: testIndexName2,
332
- queryVector: [1, 0, 0, 0],
333
- });
334
- expect(results).toEqual(results2);
335
- expect(results.length).toBeGreaterThan(0);
336
- });
337
-
338
- it('handles null filter', async () => {
339
- const results = await retryQuery({
340
- indexName: testIndexName2,
341
- queryVector: [1, 0, 0, 0],
342
- filter: null,
343
- });
344
- const results2 = await retryQuery({
345
- indexName: testIndexName2,
346
- queryVector: [1, 0, 0, 0],
347
- });
348
- expect(results).toEqual(results2);
349
- expect(results.length).toBeGreaterThan(0);
350
- });
351
-
352
- it('handles filters with multiple properties', async () => {
353
- const results = await retryQuery({
354
- indexName: testIndexName2,
355
- queryVector: [1, 0, 0, 0],
356
- filter: {
357
- 'metadata.label': 'test_filter_validation',
358
- 'metadata.timestamp': { $gt: new Date('2023-01-01T00:00:00Z') },
359
- },
360
- });
361
- expect(results.length).toBeGreaterThan(0);
362
- });
363
-
364
- it('normalizes date values in filter using filter.ts', async () => {
365
- const vector = [1, 0, 0, 0];
366
- const timestampDate = new Date('2024-01-01T00:00:00Z');
367
- // Upsert a document with a timestamp in metadata
368
- await vectorDB.upsert({
369
- indexName: testIndexName2,
370
- vectors: [vector],
371
- metadata: [{ timestamp: timestampDate }],
372
- });
373
- await new Promise(r => setTimeout(r, 5000));
374
- const results = await retryQuery({
375
- indexName: testIndexName2,
376
- queryVector: vector,
377
- filter: { 'metadata.timestamp': { $gt: new Date('2023-01-01T00:00:00Z') } },
378
- });
379
- expect(results.length).toBeGreaterThan(0);
380
- expect(new Date(results[0]?.metadata?.timestamp).toISOString()).toEqual(timestampDate.toISOString());
381
- });
382
- });
383
-
384
- describe('Basic vector operations', () => {
385
- const indexName = 'test_basic_vector_ops_' + Date.now();
386
- beforeAll(async () => {
387
- await createIndexAndWait(vectorDB, indexName, 4, 'cosine');
388
- });
389
- afterAll(async () => {
390
- await deleteIndexAndWait(vectorDB, indexName);
391
- });
392
- const testVectors = [
393
- [1, 0, 0, 0],
394
- [0, 1, 0, 0],
395
- [0, 0, 1, 0],
396
- [0, 0, 0, 1],
397
- ];
398
- it('should update the vector by id', async () => {
399
- const ids = await vectorDB.upsert({ indexName, vectors: testVectors });
400
- expect(ids).toHaveLength(4);
401
- const idToBeUpdated = ids[0];
402
- const newVector = [1, 2, 3, 4];
403
- const newMetaData = { test: 'updates' };
404
- await vectorDB.updateVector({
405
- indexName,
406
- id: idToBeUpdated,
407
- update: { vector: newVector, metadata: newMetaData },
408
- });
409
- await new Promise(resolve => setTimeout(resolve, 5000));
410
- const results = await vectorDB.query({
411
- indexName,
412
- queryVector: newVector,
413
- topK: 2,
414
- includeVector: true,
415
- });
416
- expect(results).toHaveLength(2);
417
- const updatedResult = results.find(result => result.id === idToBeUpdated);
418
- expect(updatedResult).toBeDefined();
419
- expect(updatedResult?.id).toEqual(idToBeUpdated);
420
- expect(updatedResult?.vector).toEqual(newVector);
421
- expect(updatedResult?.metadata).toEqual(newMetaData);
422
- });
423
- it('should only update the metadata by id', async () => {
424
- const ids = await vectorDB.upsert({ indexName, vectors: testVectors });
425
- expect(ids).toHaveLength(4);
426
- const idToBeUpdated = ids[0];
427
- const newMetaData = { test: 'metadata only update' };
428
- await vectorDB.updateVector({ indexName, id: idToBeUpdated, update: { metadata: newMetaData } });
429
- await new Promise(resolve => setTimeout(resolve, 5000));
430
- const results = await vectorDB.query({
431
- indexName,
432
- queryVector: testVectors[0],
433
- topK: 2,
434
- includeVector: true,
435
- });
436
- expect(results).toHaveLength(2);
437
- const updatedResult = results.find(result => result.id === idToBeUpdated);
438
- expect(updatedResult).toBeDefined();
439
- expect(updatedResult?.id).toEqual(idToBeUpdated);
440
- expect(updatedResult?.vector).toEqual(testVectors[0]);
441
- expect(updatedResult?.metadata).toEqual(newMetaData);
442
- });
443
- it('should only update vector embeddings by id', async () => {
444
- const ids = await vectorDB.upsert({ indexName, vectors: testVectors });
445
- expect(ids).toHaveLength(4);
446
- const idToBeUpdated = ids[0];
447
- const newVector = [1, 2, 3, 4];
448
- await vectorDB.updateVector({ indexName, id: idToBeUpdated, update: { vector: newVector } });
449
- await new Promise(resolve => setTimeout(resolve, 5000));
450
- const results = await vectorDB.query({
451
- indexName,
452
- queryVector: newVector,
453
- topK: 2,
454
- includeVector: true,
455
- });
456
- expect(results).toHaveLength(2);
457
- const updatedResult = results.find(result => result.id === idToBeUpdated);
458
- expect(updatedResult).toBeDefined();
459
- expect(updatedResult?.id).toEqual(idToBeUpdated);
460
- expect(updatedResult?.vector).toEqual(newVector);
461
- });
462
- it('should throw exception when no updates are given', async () => {
463
- await expect(vectorDB.updateVector({ indexName, id: 'nonexistent-id', update: {} })).rejects.toThrow(
464
- 'No updates provided',
465
- );
466
- });
467
- it('should delete the vector by id', async () => {
468
- const ids = await vectorDB.upsert({ indexName, vectors: testVectors });
469
- expect(ids).toHaveLength(4);
470
- const idToBeDeleted = ids[0];
471
-
472
- const initialStats = await vectorDB.describeIndex({ indexName });
473
-
474
- await vectorDB.deleteVector({ indexName, id: idToBeDeleted });
475
- const results = await vectorDB.query({ indexName, queryVector: [1, 0, 0, 0], topK: 2 });
476
- expect(results.map(res => res.id)).not.toContain(idToBeDeleted);
477
-
478
- await new Promise(resolve => setTimeout(resolve, 5000)); // Wait for count to update
479
- const finalStats = await vectorDB.describeIndex({ indexName });
480
- expect(finalStats.count).toBe(initialStats.count - 1);
481
- });
482
- });
483
-
484
- describe('Error Handling', () => {
485
- it('should handle non-existent index queries', async () => {
486
- await expect(vectorDB.query({ indexName: 'non-existent-index', queryVector: [1, 2, 3] })).rejects.toThrow();
487
- });
488
-
489
- it('should handle invalid dimension vectors', async () => {
490
- const invalidVector = [1, 2, 3]; // 3D vector for 4D index
491
- await expect(vectorDB.upsert({ indexName: testIndexName, vectors: [invalidVector] })).rejects.toThrow();
492
- });
493
- it('should return empty results and not throw when semantic search filter matches zero documents', async () => {
494
- // Use a valid embedding vector matching your test index dimension
495
- const testEmbedding = [0.1, 0.2, 0.3, 0.4]; // Adjust dimension as needed
496
-
497
- // Should not throw, should return an empty array
498
- let error: unknown = null;
499
- let results: any[] = [];
500
- try {
501
- results = await vectorDB.query({
502
- indexName: emptyIndexName,
503
- queryVector: testEmbedding,
504
- topK: 2,
505
- filter: {
506
- 'metadata.label': 'test_filter_validation',
507
- },
508
- });
509
- } catch (e) {
510
- error = e;
511
- }
512
-
513
- expect(error).toBeNull();
514
- expect(Array.isArray(results)).toBe(true);
515
- expect(results.length).toBe(0);
516
- });
517
- });
518
-
519
- describe('Metadata Field Filtering Bug Reproduction', () => {
520
- const bugTestIndexName = 'metadata_filter_bug_test_' + Date.now();
521
-
522
- beforeAll(async () => {
523
- // Create index for bug reproduction
524
- await createIndexAndWait(vectorDB, bugTestIndexName, 4, 'cosine');
525
-
526
- // Insert vectors with thread_id and resource_id in metadata
527
- // Simulating what the Memory system does
528
- const vectors = [
529
- [1, 0, 0, 0],
530
- [0, 1, 0, 0],
531
- [0, 0, 1, 0],
532
- [0, 0, 0, 1],
533
- ];
534
-
535
- const metadata = [
536
- { thread_id: 'thread-123', resource_id: 'resource-123', message: 'first' },
537
- { thread_id: 'thread-123', resource_id: 'resource-123', message: 'second' },
538
- { thread_id: 'thread-456', resource_id: 'resource-456', message: 'third' },
539
- { thread_id: 'thread-456', resource_id: 'resource-456', message: 'fourth' },
540
- ];
541
-
542
- await vectorDB.upsert({
543
- indexName: bugTestIndexName,
544
- vectors,
545
- metadata,
546
- });
547
-
548
- // Wait for indexing
549
- await new Promise(resolve => setTimeout(resolve, 5000));
550
- });
551
-
552
- afterAll(async () => {
553
- await deleteIndexAndWait(vectorDB, bugTestIndexName);
554
- });
555
-
556
- test('filtering by thread_id WITHOUT metadata prefix works correctly', async () => {
557
- // This is what Memory.rememberMessages does - passes thread_id directly
558
- // Previously this would ignore the filter, but now it works correctly
559
- const results = await vectorDB.query({
560
- indexName: bugTestIndexName,
561
- queryVector: [1, 0, 0, 0],
562
- topK: 10,
563
- filter: { thread_id: 'thread-123' }, // Now correctly filters by thread_id
564
- });
565
-
566
- // Verify the fix works - should return only documents from thread-123
567
- expect(results).toHaveLength(2);
568
- expect(results.every(r => r.metadata?.thread_id === 'thread-123')).toBe(true);
569
-
570
- // Should NOT contain documents from other threads
571
- const threadIds = results.map(r => r.metadata?.thread_id);
572
- expect(threadIds).not.toContain('thread-456');
573
- });
574
-
575
- test('filtering by resource_id WITHOUT metadata prefix works correctly', async () => {
576
- // This is what Memory.rememberMessages does with resource scope
577
- const results = await vectorDB.query({
578
- indexName: bugTestIndexName,
579
- queryVector: [0, 1, 0, 0],
580
- topK: 10,
581
- filter: { resource_id: 'resource-123' }, // Now correctly filters by resource_id
582
- });
583
-
584
- // Verify the fix works - should return only documents from resource-123
585
- expect(results).toHaveLength(2);
586
- expect(results.every(r => r.metadata?.resource_id === 'resource-123')).toBe(true);
587
-
588
- // Should NOT contain documents from other resources
589
- const resourceIds = results.map(r => r.metadata?.resource_id);
590
- expect(resourceIds).not.toContain('resource-456');
591
- });
592
-
593
- test('filtering WITH metadata. prefix works correctly (workaround)', async () => {
594
- // This is the workaround - using metadata.thread_id
595
- const results = await vectorDB.query({
596
- indexName: bugTestIndexName,
597
- queryVector: [1, 0, 0, 0],
598
- topK: 10,
599
- filter: { 'metadata.thread_id': 'thread-123' },
600
- });
601
-
602
- // This works correctly
603
- expect(results).toHaveLength(2);
604
- expect(results[0]?.metadata?.thread_id).toBe('thread-123');
605
- expect(results[1]?.metadata?.thread_id).toBe('thread-123');
606
- });
607
-
608
- test('semantic search without filter returns all vectors (shows data exists)', async () => {
609
- // Verify that the data exists and can be retrieved without filters
610
- const results = await vectorDB.query({
611
- indexName: bugTestIndexName,
612
- queryVector: [0.5, 0.5, 0.5, 0.5],
613
- topK: 10,
614
- });
615
-
616
- // Should return all 4 vectors
617
- expect(results).toHaveLength(4);
618
-
619
- // Verify metadata is stored correctly
620
- const threadIds = results.map(r => r.metadata?.thread_id);
621
- expect(threadIds).toContain('thread-123');
622
- expect(threadIds).toContain('thread-456');
623
- });
624
- });
625
- });
626
-
627
- // Use the shared test suite with factory pattern
628
- const vectorDB = new MongoDBVector({ uri, dbName });
629
-
630
- createVectorTestSuite({
631
- vector: vectorDB,
632
- connect: async () => {
633
- await vectorDB.connect();
634
- await waitForAtlasSearchReady(vectorDB);
635
- },
636
- disconnect: async () => {
637
- await vectorDB.disconnect();
638
- },
639
- createIndex: async (indexName: string) => {
640
- await vectorDB.createIndex({ indexName, dimension: 4, metric: 'cosine' });
641
- await vectorDB.waitForIndexReady({ indexName });
642
- },
643
- deleteIndex: async (indexName: string) => {
644
- try {
645
- await vectorDB.deleteIndex({ indexName });
646
- } catch (error) {
647
- console.error(`Error deleting index ${indexName}:`, error);
648
- }
649
- },
650
- waitForIndexing: () => new Promise(resolve => setTimeout(resolve, 5000)),
651
- });