@mastra/chroma 0.1.6-alpha.0 → 0.1.6-alpha.3

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,5 +1,5 @@
1
- import { QueryResult, IndexStats } from '@mastra/core/vector';
2
- import { describe, expect, beforeEach, afterEach, it, beforeAll, afterAll } from 'vitest';
1
+ import type { QueryResult, IndexStats } from '@mastra/core/vector';
2
+ import { describe, expect, beforeEach, afterEach, it, beforeAll, afterAll, vi } from 'vitest';
3
3
 
4
4
  import { ChromaVector } from './';
5
5
 
@@ -10,23 +10,24 @@ describe('ChromaVector Integration Tests', () => {
10
10
 
11
11
  const testIndexName = 'test-index';
12
12
  const testIndexName2 = 'test-index-2';
13
+ const testIndexName3 = 'test-index-3';
13
14
  const dimension = 3;
14
15
 
15
16
  beforeEach(async () => {
16
17
  // Clean up any existing test index
17
18
  try {
18
19
  await vectorDB.deleteIndex(testIndexName);
19
- } catch (error) {
20
+ } catch {
20
21
  // Ignore errors if index doesn't exist
21
22
  }
22
- await vectorDB.createIndex(testIndexName, dimension);
23
+ await vectorDB.createIndex({ indexName: testIndexName, dimension });
23
24
  }, 5000);
24
25
 
25
26
  afterEach(async () => {
26
27
  // Cleanup after tests
27
28
  try {
28
29
  await vectorDB.deleteIndex(testIndexName);
29
- } catch (error) {
30
+ } catch {
30
31
  // Ignore cleanup errors
31
32
  }
32
33
  }, 5000);
@@ -55,7 +56,7 @@ describe('ChromaVector Integration Tests', () => {
55
56
 
56
57
  for (const metric of metricsToTest) {
57
58
  const testIndex = `test-index-${metric}`;
58
- await vectorDB.createIndex(testIndex, dimension, metric);
59
+ await vectorDB.createIndex({ indexName: testIndex, dimension, metric });
59
60
 
60
61
  const stats = await vectorDB.describeIndex(testIndex);
61
62
  expect(stats.metric).toBe(metric);
@@ -75,7 +76,7 @@ describe('ChromaVector Integration Tests', () => {
75
76
  const testIds = ['vec1', 'vec2', 'vec3'];
76
77
 
77
78
  it('should upsert vectors with generated ids', async () => {
78
- const ids = await vectorDB.upsert(testIndexName, testVectors);
79
+ const ids = await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors });
79
80
  expect(ids).toHaveLength(testVectors.length);
80
81
  ids.forEach(id => expect(typeof id).toBe('string'));
81
82
 
@@ -84,14 +85,14 @@ describe('ChromaVector Integration Tests', () => {
84
85
  });
85
86
 
86
87
  it('should upsert vectors with provided ids and metadata', async () => {
87
- await vectorDB.upsert(testIndexName, testVectors, testMetadata, testIds);
88
+ await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors, metadata: testMetadata, ids: testIds });
88
89
 
89
90
  const stats = await vectorDB.describeIndex(testIndexName);
90
91
  expect(stats.count).toBe(testVectors.length);
91
92
 
92
93
  // Query each vector to verify metadata
93
94
  for (let i = 0; i < testVectors.length; i++) {
94
- const results = await vectorDB.query(testIndexName, testVectors?.[i]!, 1);
95
+ const results = await vectorDB.query({ indexName: testIndexName, queryVector: testVectors?.[i]!, topK: 1 });
95
96
  expect(results?.[0]?.id).toBe(testIds[i]);
96
97
  expect(results?.[0]?.metadata).toEqual(testMetadata[i]);
97
98
  }
@@ -99,15 +100,20 @@ describe('ChromaVector Integration Tests', () => {
99
100
 
100
101
  it('should update existing vectors', async () => {
101
102
  // Initial upsert
102
- await vectorDB.upsert(testIndexName, testVectors, testMetadata, testIds);
103
+ await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors, metadata: testMetadata, ids: testIds });
103
104
 
104
105
  // Update first vector
105
106
  const updatedVector = [[0.5, 0.5, 0.0]];
106
107
  const updatedMetadata = [{ label: 'updated-x-axis' }];
107
- await vectorDB.upsert(testIndexName, updatedVector, updatedMetadata, [testIds?.[0]!]);
108
+ await vectorDB.upsert({
109
+ indexName: testIndexName,
110
+ vectors: updatedVector,
111
+ metadata: updatedMetadata,
112
+ ids: [testIds?.[0]!],
113
+ });
108
114
 
109
115
  // Verify update
110
- const results = await vectorDB.query(testIndexName, updatedVector?.[0]!, 1);
116
+ const results = await vectorDB.query({ indexName: testIndexName, queryVector: updatedVector?.[0]!, topK: 1 });
111
117
  expect(results?.[0]?.id).toBe(testIds[0]);
112
118
  expect(results?.[0]?.metadata).toEqual(updatedMetadata[0]);
113
119
  });
@@ -123,7 +129,7 @@ describe('ChromaVector Integration Tests', () => {
123
129
  const testIds = ['vec1', 'vec2', 'vec3'];
124
130
 
125
131
  beforeEach(async () => {
126
- await vectorDB.upsert(testIndexName, testVectors, testMetadata, testIds);
132
+ await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors, metadata: testMetadata, ids: testIds });
127
133
  });
128
134
 
129
135
  describe('Basic Queries', () => {
@@ -131,7 +137,7 @@ describe('ChromaVector Integration Tests', () => {
131
137
  const queryVector = [1.0, 0.1, 0.1];
132
138
  const topK = 2;
133
139
 
134
- const results: QueryResult[] = await vectorDB.query(testIndexName, queryVector, topK);
140
+ const results: QueryResult[] = await vectorDB.query({ indexName: testIndexName, queryVector, topK });
135
141
 
136
142
  expect(results).toHaveLength(topK);
137
143
  expect(results?.[0]?.id).toBe(testIds[0]); // Should match x-axis vector most closely
@@ -143,7 +149,7 @@ describe('ChromaVector Integration Tests', () => {
143
149
  const queryVector = [1.0, 1.0, 1.0];
144
150
  const filter = { label: 'x-axis' };
145
151
 
146
- const results = await vectorDB.query(testIndexName, queryVector, 3, filter);
152
+ const results = await vectorDB.query({ indexName: testIndexName, queryVector, topK: 3, filter });
147
153
 
148
154
  expect(results).toHaveLength(1);
149
155
  expect(results?.[0]?.metadata?.label).toBe('x-axis');
@@ -155,7 +161,12 @@ describe('ChromaVector Integration Tests', () => {
155
161
  const queryVector = [1.0, 0.1, 0.1];
156
162
  const topK = 1;
157
163
 
158
- const results = await vectorDB.query(testIndexName, queryVector, topK, undefined, true);
164
+ const results = await vectorDB.query({
165
+ indexName: testIndexName,
166
+ queryVector,
167
+ topK,
168
+ includeVector: true,
169
+ });
159
170
 
160
171
  expect(results).toHaveLength(topK);
161
172
  expect(results?.[0]?.vector).toEqual(testVectors[0]);
@@ -165,73 +176,95 @@ describe('ChromaVector Integration Tests', () => {
165
176
 
166
177
  describe('Error Handling', () => {
167
178
  it('should handle non-existent index queries', async () => {
168
- await expect(vectorDB.query('non-existent-index-yu', [1, 2, 3])).rejects.toThrow();
179
+ await expect(vectorDB.query({ indexName: 'non-existent-index-yu', queryVector: [1, 2, 3] })).rejects.toThrow();
169
180
  });
170
181
 
171
182
  it('should handle invalid dimension vectors', async () => {
172
183
  const invalidVector = [1, 2, 3, 4]; // 4D vector for 3D index
173
- await expect(vectorDB.upsert(testIndexName, [invalidVector])).rejects.toThrow();
184
+ await expect(vectorDB.upsert({ indexName: testIndexName, vectors: [invalidVector] })).rejects.toThrow();
174
185
  });
175
186
 
176
187
  it('should handle mismatched metadata and vectors length', async () => {
177
188
  const vectors = [[1, 2, 3]];
178
189
  const metadata = [{}, {}]; // More metadata than vectors
179
- await expect(vectorDB.upsert(testIndexName, vectors, metadata)).rejects.toThrow();
190
+ await expect(vectorDB.upsert({ indexName: testIndexName, vectors, metadata })).rejects.toThrow();
180
191
  });
181
192
  });
182
193
 
183
194
  describe('Filter Validation in Queries', () => {
184
195
  it('rejects queries with null values', async () => {
185
196
  await expect(
186
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
187
- field: null,
197
+ vectorDB.query({
198
+ indexName: testIndexName,
199
+ queryVector: [1, 0, 0],
200
+ filter: {
201
+ field: null,
202
+ },
188
203
  }),
189
204
  ).rejects.toThrow();
190
205
 
191
206
  await expect(
192
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
193
- other: { $eq: null },
207
+ vectorDB.query({
208
+ indexName: testIndexName,
209
+ queryVector: [1, 0, 0],
210
+ filter: {
211
+ other: { $eq: null },
212
+ },
194
213
  }),
195
214
  ).rejects.toThrow();
196
215
  });
197
216
 
198
217
  it('validates array operator values', async () => {
199
218
  await expect(
200
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
201
- tags: { $in: null },
219
+ vectorDB.query({
220
+ indexName: testIndexName,
221
+ queryVector: [1, 0, 0],
222
+ filter: {
223
+ tags: { $in: null },
224
+ },
202
225
  }),
203
226
  ).rejects.toThrow();
204
227
  });
205
228
 
206
229
  it('validates numeric values for comparison operators', async () => {
207
230
  await expect(
208
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
209
- price: { $gt: 'not-a-number' },
231
+ vectorDB.query({
232
+ indexName: testIndexName,
233
+ queryVector: [1, 0, 0],
234
+ filter: {
235
+ price: { $gt: 'not-a-number' },
236
+ },
210
237
  }),
211
238
  ).rejects.toThrow();
212
239
  });
213
240
 
214
241
  it('validates value types', async () => {
215
242
  await expect(
216
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
217
- date: { $gt: 'not-a-date' },
243
+ vectorDB.query({
244
+ indexName: testIndexName,
245
+ queryVector: [1, 0, 0],
246
+ filter: { date: { $gt: 'not-a-date' } },
218
247
  }),
219
248
  ).rejects.toThrow();
220
249
 
221
250
  await expect(
222
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
223
- number: { $lt: 'not-a-number' },
251
+ vectorDB.query({
252
+ indexName: testIndexName,
253
+ queryVector: [1, 0, 0],
254
+ filter: { number: { $lt: 'not-a-number' } },
224
255
  }),
225
256
  ).rejects.toThrow();
226
257
  });
227
258
 
228
259
  it('validates array operators', async () => {
229
- const invalidValues = [123, 'string', true, { key: 'value' }, null, undefined];
260
+ const invalidValues = [123, 'string', true, { key: 'value' }, null];
230
261
  for (const op of ['$in', '$nin']) {
231
262
  for (const val of invalidValues) {
232
263
  await expect(
233
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
234
- field: { [op]: val },
264
+ vectorDB.query({
265
+ indexName: testIndexName,
266
+ queryVector: [1, 0, 0],
267
+ filter: { field: { [op]: val } },
235
268
  }),
236
269
  ).rejects.toThrow();
237
270
  }
@@ -244,8 +277,10 @@ describe('ChromaVector Integration Tests', () => {
244
277
  for (const op of ['$in', '$nin']) {
245
278
  for (const val of invalidValues) {
246
279
  await expect(
247
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
248
- field: { [op]: val },
280
+ vectorDB.query({
281
+ indexName: testIndexName,
282
+ queryVector: [1, 0, 0],
283
+ filter: { field: { [op]: val } },
249
284
  }),
250
285
  ).rejects.toThrow();
251
286
  }
@@ -256,8 +291,10 @@ describe('ChromaVector Integration Tests', () => {
256
291
  // Basic equality can accept any non-undefined value
257
292
  for (const op of ['$eq', '$ne']) {
258
293
  await expect(
259
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
260
- field: { [op]: undefined },
294
+ vectorDB.query({
295
+ indexName: testIndexName,
296
+ queryVector: [1, 0, 0],
297
+ filter: { field: { [op]: undefined } },
261
298
  }),
262
299
  ).rejects.toThrow();
263
300
  }
@@ -268,8 +305,10 @@ describe('ChromaVector Integration Tests', () => {
268
305
  for (const op of numOps) {
269
306
  for (const val of invalidNumericValues) {
270
307
  await expect(
271
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
272
- field: { [op]: val },
308
+ vectorDB.query({
309
+ indexName: testIndexName,
310
+ queryVector: [1, 0, 0],
311
+ filter: { field: { [op]: val } },
273
312
  }),
274
313
  ).rejects.toThrow();
275
314
  }
@@ -278,29 +317,45 @@ describe('ChromaVector Integration Tests', () => {
278
317
 
279
318
  it('validates multiple invalid values', async () => {
280
319
  await expect(
281
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
282
- field1: { $in: 'not-array' },
283
- field2: { $exists: 'not-boolean' },
284
- field3: { $gt: 'not-number' },
320
+ vectorDB.query({
321
+ indexName: testIndexName,
322
+ queryVector: [1, 0, 0],
323
+ filter: {
324
+ field1: { $in: 'not-array' },
325
+ field2: { $exists: 'not-boolean' },
326
+ field3: { $gt: 'not-number' },
327
+ },
285
328
  }),
286
329
  ).rejects.toThrow();
287
330
  });
288
331
 
289
332
  it('handles empty object filters', async () => {
290
333
  // Test empty object at top level
291
- await expect(vectorDB.query(testIndexName, [1, 0, 0], 10, { field: { $eq: {} } })).rejects.toThrow();
334
+ await expect(
335
+ vectorDB.query({
336
+ indexName: testIndexName,
337
+ queryVector: [1, 0, 0],
338
+ filter: { field: { $eq: {} } },
339
+ }),
340
+ ).rejects.toThrow();
292
341
  });
293
342
 
294
343
  it('handles empty/undefined filters by returning all results', async () => {
295
344
  const noFilterCases = [{ field: {} }, { field: undefined }, { field: { $in: undefined } }];
296
345
 
297
346
  for (const filter of noFilterCases) {
298
- await expect(vectorDB.query(testIndexName, [1, 0, 0], 10, filter)).rejects.toThrow();
347
+ await expect(vectorDB.query({ indexName: testIndexName, queryVector: [1, 0, 0], filter })).rejects.toThrow();
299
348
  }
300
349
  });
301
350
  it('handles empty object filters', async () => {
302
351
  // Test empty object at top level
303
- await expect(vectorDB.query(testIndexName, [1, 0, 0], 10, {})).rejects.toThrow();
352
+ await expect(
353
+ vectorDB.query({
354
+ indexName: testIndexName,
355
+ queryVector: [1, 0, 0],
356
+ filter: {},
357
+ }),
358
+ ).rejects.toThrow();
304
359
  });
305
360
  });
306
361
 
@@ -309,10 +364,10 @@ describe('ChromaVector Integration Tests', () => {
309
364
  beforeAll(async () => {
310
365
  try {
311
366
  await vectorDB.deleteIndex(testIndexName2);
312
- } catch (error) {
367
+ } catch {
313
368
  // Ignore errors if index doesn't exist
314
369
  }
315
- await vectorDB.createIndex(testIndexName2, dimension);
370
+ await vectorDB.createIndex({ indexName: testIndexName2, dimension });
316
371
 
317
372
  const vectors = [
318
373
  [1, 0, 0], // Electronics
@@ -348,7 +403,7 @@ describe('ChromaVector Integration Tests', () => {
348
403
  },
349
404
  ];
350
405
 
351
- await vectorDB.upsert(testIndexName2, vectors, metadata);
406
+ await vectorDB.upsert({ indexName: testIndexName2, vectors, metadata });
352
407
  // Wait for indexing
353
408
  await new Promise(resolve => setTimeout(resolve, 2000));
354
409
  });
@@ -357,15 +412,17 @@ describe('ChromaVector Integration Tests', () => {
357
412
  // Cleanup after tests
358
413
  try {
359
414
  await vectorDB.deleteIndex(testIndexName2);
360
- } catch (error) {
415
+ } catch {
361
416
  // Ignore cleanup errors
362
417
  }
363
418
  });
364
419
 
365
420
  describe('Basic Comparison Operators', () => {
366
421
  it('filters with $eq operator', async () => {
367
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
368
- category: { $eq: 'electronics' },
422
+ const results = await vectorDB.query({
423
+ indexName: testIndexName2,
424
+ queryVector: [1, 0, 0],
425
+ filter: { category: { $eq: 'electronics' } },
369
426
  });
370
427
  expect(results.length).toBe(2);
371
428
  results.forEach(result => {
@@ -374,8 +431,10 @@ describe('ChromaVector Integration Tests', () => {
374
431
  });
375
432
 
376
433
  it('filters with implicit $eq', async () => {
377
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
378
- category: 'electronics', // implicit $eq
434
+ const results = await vectorDB.query({
435
+ indexName: testIndexName2,
436
+ queryVector: [1, 0, 0],
437
+ filter: { category: 'electronics' }, // implicit $eq
379
438
  });
380
439
  expect(results.length).toBe(2);
381
440
  results.forEach(result => {
@@ -383,8 +442,10 @@ describe('ChromaVector Integration Tests', () => {
383
442
  });
384
443
  });
385
444
  it('filters with $gt operator', async () => {
386
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
387
- price: { $gt: 500 },
445
+ const results = await vectorDB.query({
446
+ indexName: testIndexName2,
447
+ queryVector: [1, 0, 0],
448
+ filter: { price: { $gt: 500 } },
388
449
  });
389
450
  expect(results.length).toBe(1);
390
451
  results.forEach(result => {
@@ -393,8 +454,10 @@ describe('ChromaVector Integration Tests', () => {
393
454
  });
394
455
 
395
456
  it('filters with $gte operator', async () => {
396
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
397
- price: { $gte: 500 },
457
+ const results = await vectorDB.query({
458
+ indexName: testIndexName2,
459
+ queryVector: [1, 0, 0],
460
+ filter: { price: { $gte: 500 } },
398
461
  });
399
462
  expect(results.length).toBe(2);
400
463
  results.forEach(result => {
@@ -403,8 +466,10 @@ describe('ChromaVector Integration Tests', () => {
403
466
  });
404
467
 
405
468
  it('filters with $lt operator', async () => {
406
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
407
- price: { $lt: 100 },
469
+ const results = await vectorDB.query({
470
+ indexName: testIndexName2,
471
+ queryVector: [1, 0, 0],
472
+ filter: { price: { $lt: 100 } },
408
473
  });
409
474
  expect(results.length).toBe(2);
410
475
  results.forEach(result => {
@@ -413,8 +478,10 @@ describe('ChromaVector Integration Tests', () => {
413
478
  });
414
479
 
415
480
  it('filters with $lte operator', async () => {
416
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
417
- price: { $lte: 500 },
481
+ const results = await vectorDB.query({
482
+ indexName: testIndexName2,
483
+ queryVector: [1, 0, 0],
484
+ filter: { price: { $lte: 500 } },
418
485
  });
419
486
  expect(results.length).toBeGreaterThan(0);
420
487
  results.forEach(result => {
@@ -423,8 +490,10 @@ describe('ChromaVector Integration Tests', () => {
423
490
  });
424
491
 
425
492
  it('filters with $gte, $lt, $lte operators', async () => {
426
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
427
- price: { $gte: 25, $lte: 500 },
493
+ const results = await vectorDB.query({
494
+ indexName: testIndexName2,
495
+ queryVector: [1, 0, 0],
496
+ filter: { price: { $gte: 25, $lte: 500 } },
428
497
  });
429
498
  expect(results.length).toBe(2);
430
499
  results.forEach(result => {
@@ -434,8 +503,10 @@ describe('ChromaVector Integration Tests', () => {
434
503
  });
435
504
 
436
505
  it('filters with $ne operator', async () => {
437
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
438
- category: { $ne: 'electronics' },
506
+ const results = await vectorDB.query({
507
+ indexName: testIndexName2,
508
+ queryVector: [1, 0, 0],
509
+ filter: { category: { $ne: 'electronics' } },
439
510
  });
440
511
  expect(results.length).toBe(2);
441
512
  results.forEach(result => {
@@ -444,8 +515,10 @@ describe('ChromaVector Integration Tests', () => {
444
515
  });
445
516
 
446
517
  it('filters with boolean values', async () => {
447
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
448
- inStock: true, // test both implicit
518
+ const results = await vectorDB.query({
519
+ indexName: testIndexName2,
520
+ queryVector: [1, 0, 0],
521
+ filter: { inStock: true }, // test both implicit
449
522
  });
450
523
  expect(results.length).toBe(3);
451
524
  results.forEach(result => {
@@ -454,9 +527,10 @@ describe('ChromaVector Integration Tests', () => {
454
527
  });
455
528
 
456
529
  it('filters with multiple fields', async () => {
457
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
458
- category: 'electronics',
459
- price: 1000,
530
+ const results = await vectorDB.query({
531
+ indexName: testIndexName2,
532
+ queryVector: [1, 0, 0],
533
+ filter: { category: 'electronics', price: 1000 },
460
534
  });
461
535
  expect(results.length).toBeGreaterThan(0);
462
536
  results.forEach(result => {
@@ -467,8 +541,10 @@ describe('ChromaVector Integration Tests', () => {
467
541
 
468
542
  describe('Array Operators', () => {
469
543
  it('filters with $in operator', async () => {
470
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
471
- category: { $in: ['electronics', 'books'] },
544
+ const results = await vectorDB.query({
545
+ indexName: testIndexName2,
546
+ queryVector: [1, 0, 0],
547
+ filter: { category: { $in: ['electronics', 'books'] } },
472
548
  });
473
549
  expect(results.length).toBeGreaterThan(0);
474
550
  results.forEach(result => {
@@ -477,8 +553,10 @@ describe('ChromaVector Integration Tests', () => {
477
553
  });
478
554
 
479
555
  it('should filter with $in operator for numbers', async () => {
480
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
481
- price: { $in: [50, 75, 1000] },
556
+ const results = await vectorDB.query({
557
+ indexName: testIndexName2,
558
+ queryVector: [1, 0, 0],
559
+ filter: { price: { $in: [50, 75, 1000] } },
482
560
  });
483
561
  expect(results.length).toBeGreaterThan(0);
484
562
  results.forEach(result => {
@@ -487,8 +565,10 @@ describe('ChromaVector Integration Tests', () => {
487
565
  });
488
566
 
489
567
  it('filters with $in operator for booleans', async () => {
490
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
491
- inStock: { $in: [true] },
568
+ const results = await vectorDB.query({
569
+ indexName: testIndexName2,
570
+ queryVector: [1, 0, 0],
571
+ filter: { inStock: { $in: [true] } },
492
572
  });
493
573
  expect(results.length).toBeGreaterThan(0);
494
574
  results.forEach(result => {
@@ -499,8 +579,10 @@ describe('ChromaVector Integration Tests', () => {
499
579
 
500
580
  describe('Logical Operators', () => {
501
581
  it('filters with $and operator', async () => {
502
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
503
- $and: [{ category: 'electronics' }, { price: { $gt: 500 } }],
582
+ const results = await vectorDB.query({
583
+ indexName: testIndexName2,
584
+ queryVector: [1, 0, 0],
585
+ filter: { $and: [{ category: 'electronics' }, { price: { $gt: 500 } }] },
504
586
  });
505
587
  expect(results.length).toBe(1);
506
588
  expect(results[0]?.metadata?.category).toBe('electronics');
@@ -508,8 +590,10 @@ describe('ChromaVector Integration Tests', () => {
508
590
  });
509
591
 
510
592
  it('should filter with $and operator', async () => {
511
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
512
- $and: [{ category: 'electronics' }, { price: { $gt: 700 } }, { inStock: true }],
593
+ const results = await vectorDB.query({
594
+ indexName: testIndexName2,
595
+ queryVector: [1, 0, 0],
596
+ filter: { $and: [{ category: 'electronics' }, { price: { $gt: 700 } }, { inStock: true }] },
513
597
  });
514
598
  expect(results.length).toBeGreaterThan(0);
515
599
  results.forEach(result => {
@@ -520,8 +604,10 @@ describe('ChromaVector Integration Tests', () => {
520
604
  });
521
605
 
522
606
  it('filters with $or operator', async () => {
523
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
524
- $or: [{ price: { $gt: 900 } }, { rating: { $gt: 4.8 } }],
607
+ const results = await vectorDB.query({
608
+ indexName: testIndexName2,
609
+ queryVector: [1, 0, 0],
610
+ filter: { $or: [{ price: { $gt: 900 } }, { rating: { $gt: 4.8 } }] },
525
611
  });
526
612
  expect(results.length).toBe(2);
527
613
  results.forEach(result => {
@@ -530,8 +616,10 @@ describe('ChromaVector Integration Tests', () => {
530
616
  });
531
617
 
532
618
  it('should filter with $or operator', async () => {
533
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
534
- $or: [{ price: { $gt: 900 } }, { category: { $in: ['electronics', 'books'] } }],
619
+ const results = await vectorDB.query({
620
+ indexName: testIndexName2,
621
+ queryVector: [1, 0, 0],
622
+ filter: { $or: [{ price: { $gt: 900 } }, { category: { $in: ['electronics', 'books'] } }] },
535
623
  });
536
624
  expect(results.length).toBeGreaterThan(0);
537
625
  results.forEach(result => {
@@ -542,14 +630,18 @@ describe('ChromaVector Integration Tests', () => {
542
630
  });
543
631
 
544
632
  it('should handle nested logical operators', async () => {
545
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
546
- $and: [
547
- {
548
- $or: [{ category: 'electronics' }, { category: 'books' }],
549
- },
550
- { price: { $lt: 100 } },
551
- { inStock: true },
552
- ],
633
+ const results = await vectorDB.query({
634
+ indexName: testIndexName2,
635
+ queryVector: [1, 0, 0],
636
+ filter: {
637
+ $and: [
638
+ {
639
+ $or: [{ category: 'electronics' }, { category: 'books' }],
640
+ },
641
+ { price: { $lt: 100 } },
642
+ { inStock: true },
643
+ ],
644
+ },
553
645
  });
554
646
  expect(results.length).toBeGreaterThan(0);
555
647
  results.forEach(result => {
@@ -560,22 +652,28 @@ describe('ChromaVector Integration Tests', () => {
560
652
  });
561
653
 
562
654
  it('uses implicit $eq within $or', async () => {
563
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
564
- $or: [{ category: 'electronics' }, { price: { $gt: 100 } }],
655
+ const results = await vectorDB.query({
656
+ indexName: testIndexName2,
657
+ queryVector: [1, 0, 0],
658
+ filter: { $or: [{ category: 'electronics' }, { price: { $gt: 100 } }] },
565
659
  });
566
660
  expect(results.length).toBeGreaterThan(0);
567
661
  });
568
662
 
569
663
  it('requires multiple conditions in logical operators', async () => {
570
664
  await expect(
571
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
572
- $and: [{ category: 'electronics' }],
665
+ vectorDB.query({
666
+ indexName: testIndexName2,
667
+ queryVector: [1, 0, 0],
668
+ filter: { $and: [{ category: 'electronics' }] },
573
669
  }),
574
670
  ).rejects.toThrow();
575
671
 
576
672
  await expect(
577
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
578
- $or: [{ price: { $gt: 900 } }],
673
+ vectorDB.query({
674
+ indexName: testIndexName2,
675
+ queryVector: [1, 0, 0],
676
+ filter: { $or: [{ price: { $gt: 900 } }] },
579
677
  }),
580
678
  ).rejects.toThrow();
581
679
  });
@@ -583,14 +681,18 @@ describe('ChromaVector Integration Tests', () => {
583
681
 
584
682
  describe('Complex Filter Combinations', () => {
585
683
  it('combines multiple operators and conditions', async () => {
586
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
587
- $and: [
588
- { price: { $gt: 20 } },
589
- { inStock: true },
590
- {
591
- $or: [{ category: { $in: ['books'] } }, { rating: { $gt: 4.5 } }],
592
- },
593
- ],
684
+ const results = await vectorDB.query({
685
+ indexName: testIndexName2,
686
+ queryVector: [1, 0, 0],
687
+ filter: {
688
+ $and: [
689
+ { price: { $gt: 20 } },
690
+ { inStock: true },
691
+ {
692
+ $or: [{ category: { $in: ['books'] } }, { rating: { $gt: 4.5 } }],
693
+ },
694
+ ],
695
+ },
594
696
  });
595
697
  expect(results.length).toBeGreaterThan(0);
596
698
  results.forEach(result => {
@@ -601,15 +703,19 @@ describe('ChromaVector Integration Tests', () => {
601
703
  });
602
704
 
603
705
  it('handles complex nested conditions', async () => {
604
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
605
- $or: [
606
- {
607
- $and: [{ category: 'electronics' }, { price: { $gt: 700 } }],
608
- },
609
- {
610
- $and: [{ category: 'books' }, { price: { $lt: 20 } }],
611
- },
612
- ],
706
+ const results = await vectorDB.query({
707
+ indexName: testIndexName2,
708
+ queryVector: [1, 0, 0],
709
+ filter: {
710
+ $or: [
711
+ {
712
+ $and: [{ category: 'electronics' }, { price: { $gt: 700 } }],
713
+ },
714
+ {
715
+ $and: [{ category: 'books' }, { price: { $lt: 20 } }],
716
+ },
717
+ ],
718
+ },
613
719
  });
614
720
  expect(results.length).toBeGreaterThan(0);
615
721
  results.forEach(result => {
@@ -622,8 +728,10 @@ describe('ChromaVector Integration Tests', () => {
622
728
  });
623
729
 
624
730
  it('should combine comparison and array operators', async () => {
625
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
626
- $and: [{ price: { $gte: 500 } }, { rating: { $gt: 4.5 } }],
731
+ const results = await vectorDB.query({
732
+ indexName: testIndexName2,
733
+ queryVector: [1, 0, 0],
734
+ filter: { $and: [{ price: { $gte: 500 } }, { rating: { $gt: 4.5 } }] },
627
735
  });
628
736
  expect(results.length).toBeGreaterThan(0);
629
737
  results.forEach(result => {
@@ -633,8 +741,10 @@ describe('ChromaVector Integration Tests', () => {
633
741
  });
634
742
 
635
743
  it('should handle multiple conditions on same field', async () => {
636
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
637
- $and: [{ price: { $gte: 30 } }, { price: { $lte: 800 } }],
744
+ const results = await vectorDB.query({
745
+ indexName: testIndexName2,
746
+ queryVector: [1, 0, 0],
747
+ filter: { $and: [{ price: { $gte: 30 } }, { price: { $lte: 800 } }] },
638
748
  });
639
749
  expect(results.length).toBeGreaterThan(0);
640
750
  results.forEach(result => {
@@ -645,15 +755,19 @@ describe('ChromaVector Integration Tests', () => {
645
755
  });
646
756
 
647
757
  it('should handle deeply nested logical operators', async () => {
648
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
649
- $or: [
650
- {
651
- $and: [{ category: 'electronics' }, { price: { $gt: 700 } }, { rating: { $gt: 4.5 } }],
652
- },
653
- {
654
- $and: [{ category: 'books' }, { price: { $lt: 50 } }, { rating: { $gt: 4.0 } }],
655
- },
656
- ],
758
+ const results = await vectorDB.query({
759
+ indexName: testIndexName2,
760
+ queryVector: [1, 0, 0],
761
+ filter: {
762
+ $or: [
763
+ {
764
+ $and: [{ category: 'electronics' }, { price: { $gt: 700 } }, { rating: { $gt: 4.5 } }],
765
+ },
766
+ {
767
+ $and: [{ category: 'books' }, { price: { $lt: 50 } }, { rating: { $gt: 4.0 } }],
768
+ },
769
+ ],
770
+ },
657
771
  });
658
772
  expect(results.length).toBeGreaterThan(0);
659
773
  results.forEach(result => {
@@ -701,13 +815,19 @@ describe('ChromaVector Integration Tests', () => {
701
815
  },
702
816
  ];
703
817
 
704
- await vectorDB.upsert(testIndexName2, vectors, metadata);
818
+ await vectorDB.upsert({
819
+ indexName: testIndexName2,
820
+ vectors,
821
+ metadata,
822
+ });
705
823
  await new Promise(resolve => setTimeout(resolve, 2000));
706
824
  });
707
825
 
708
826
  it('handles special numeric values', async () => {
709
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
710
- $or: [{ zero: 0 }, { negativeZero: 0 }],
827
+ const results = await vectorDB.query({
828
+ indexName: testIndexName2,
829
+ queryVector: [1, 0, 0],
830
+ filter: { $or: [{ zero: 0 }, { negativeZero: 0 }] },
711
831
  });
712
832
  expect(results.length).toBeGreaterThan(0);
713
833
  results.forEach(result => {
@@ -717,15 +837,23 @@ describe('ChromaVector Integration Tests', () => {
717
837
  });
718
838
 
719
839
  it('handles extreme numeric values', async () => {
720
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
721
- $or: [{ maxInt: { $gte: Number.MAX_SAFE_INTEGER } }, { minInt: { $lte: Number.MIN_SAFE_INTEGER } }],
840
+ const results = await vectorDB.query({
841
+ indexName: testIndexName2,
842
+ queryVector: [1, 0, 0],
843
+ filter: {
844
+ $or: [{ maxInt: { $gte: Number.MAX_SAFE_INTEGER } }, { minInt: { $lte: Number.MIN_SAFE_INTEGER } }],
845
+ },
722
846
  });
723
847
  expect(results.length).toBe(1);
724
848
  });
725
849
 
726
850
  it('should handle numeric comparisons with decimals', async () => {
727
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
728
- rating: { $gt: 4.5 },
851
+ const results = await vectorDB.query({
852
+ indexName: testIndexName2,
853
+ queryVector: [1, 0, 0],
854
+ filter: {
855
+ rating: { $gt: 4.5 },
856
+ },
729
857
  });
730
858
  expect(results.length).toBeGreaterThan(0);
731
859
  results.forEach(result => {
@@ -734,8 +862,10 @@ describe('ChromaVector Integration Tests', () => {
734
862
  });
735
863
 
736
864
  it('should handle boolean values', async () => {
737
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
738
- inStock: { $eq: false },
865
+ const results = await vectorDB.query({
866
+ indexName: testIndexName2,
867
+ queryVector: [1, 0, 0],
868
+ filter: { inStock: { $eq: false } },
739
869
  });
740
870
  expect(results.length).toBeGreaterThan(0);
741
871
  results.forEach(result => {
@@ -747,46 +877,78 @@ describe('ChromaVector Integration Tests', () => {
747
877
  describe('Additional Validation Tests', () => {
748
878
  it('should throw error as date is not supported', async () => {
749
879
  await expect(
750
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
751
- $and: [
752
- { currentDate: { $lte: new Date().toISOString() } },
753
- { currentDate: { $gt: new Date(0).toISOString() } },
754
- ],
880
+ vectorDB.query({
881
+ indexName: testIndexName2,
882
+ queryVector: [1, 0, 0],
883
+ filter: {
884
+ $and: [
885
+ { currentDate: { $lte: new Date().toISOString() } },
886
+ { currentDate: { $gt: new Date(0).toISOString() } },
887
+ ],
888
+ },
755
889
  }),
756
890
  ).rejects.toThrow();
757
891
  });
758
892
  it('should throw error as empty array in $in operator is not supported', async () => {
759
893
  await expect(
760
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
761
- category: { $in: [] },
894
+ vectorDB.query({
895
+ indexName: testIndexName2,
896
+ queryVector: [1, 0, 0],
897
+ filter: {
898
+ category: { $in: [] },
899
+ },
762
900
  }),
763
901
  ).rejects.toThrow();
764
902
  });
765
903
  it('should reject non-numeric values in numeric comparisons', async () => {
766
904
  await expect(
767
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
768
- price: { $gt: '500' }, // string instead of number
905
+ vectorDB.query({
906
+ indexName: testIndexName2,
907
+ queryVector: [1, 0, 0],
908
+ filter: {
909
+ price: { $gt: '500' }, // string instead of number
910
+ },
769
911
  }),
770
912
  ).rejects.toThrow();
771
913
  });
772
914
 
773
915
  it('should reject mixed types in $in operator', async () => {
774
916
  await expect(
775
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
776
- field: { $in: ['string', 123] }, // mixed string and number
917
+ vectorDB.query({
918
+ indexName: testIndexName2,
919
+ queryVector: [1, 0, 0],
920
+ filter: {
921
+ field: { $in: ['string', 123] }, // mixed string and number
922
+ },
777
923
  }),
778
924
  ).rejects.toThrow();
779
925
  });
780
926
  it('should handle undefined filter', async () => {
781
- const results1 = await vectorDB.query(testIndexName2, [1, 0, 0], 10, undefined);
782
- const results2 = await vectorDB.query(testIndexName2, [1, 0, 0], 10);
927
+ const results1 = await vectorDB.query({
928
+ indexName: testIndexName2,
929
+ queryVector: [1, 0, 0],
930
+ filter: undefined,
931
+ });
932
+ const results2 = await vectorDB.query({
933
+ indexName: testIndexName2,
934
+ queryVector: [1, 0, 0],
935
+ filter: undefined,
936
+ });
783
937
  expect(results1).toEqual(results2);
784
938
  expect(results1.length).toBeGreaterThan(0);
785
939
  });
786
940
 
787
941
  it('should handle null filter', async () => {
788
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, null as any);
789
- const results2 = await vectorDB.query(testIndexName2, [1, 0, 0], 10);
942
+ const results = await vectorDB.query({
943
+ indexName: testIndexName2,
944
+ queryVector: [1, 0, 0],
945
+ filter: null,
946
+ });
947
+ const results2 = await vectorDB.query({
948
+ indexName: testIndexName2,
949
+ queryVector: [1, 0, 0],
950
+ filter: undefined,
951
+ });
790
952
  expect(results).toEqual(results2);
791
953
  expect(results.length).toBeGreaterThan(0);
792
954
  });
@@ -794,8 +956,12 @@ describe('ChromaVector Integration Tests', () => {
794
956
 
795
957
  describe('Additional Edge Cases', () => {
796
958
  it('should handle exact boundary conditions', async () => {
797
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
798
- $and: [{ price: { $gte: 25 } }, { price: { $lte: 1000 } }],
959
+ const results = await vectorDB.query({
960
+ indexName: testIndexName2,
961
+ queryVector: [1, 0, 0],
962
+ filter: {
963
+ $and: [{ price: { $gte: 25 } }, { price: { $lte: 1000 } }],
964
+ },
799
965
  });
800
966
  expect(results.length).toBeGreaterThan(0);
801
967
  expect(results.some(r => r.metadata?.price === 25)).toBe(true);
@@ -805,15 +971,19 @@ describe('ChromaVector Integration Tests', () => {
805
971
 
806
972
  describe('Additional Complex Logical Combinations', () => {
807
973
  it('should handle deeply nested $or conditions', async () => {
808
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
809
- $or: [
810
- {
811
- $and: [{ category: 'electronics' }, { $or: [{ price: { $gt: 900 } }, { rating: { $gt: 4.8 } }] }],
812
- },
813
- {
814
- $and: [{ category: 'books' }, { $or: [{ price: { $lt: 30 } }, { rating: { $gt: 4.5 } }] }],
815
- },
816
- ],
974
+ const results = await vectorDB.query({
975
+ indexName: testIndexName2,
976
+ queryVector: [1, 0, 0],
977
+ filter: {
978
+ $or: [
979
+ {
980
+ $and: [{ category: 'electronics' }, { $or: [{ price: { $gt: 900 } }, { rating: { $gt: 4.8 } }] }],
981
+ },
982
+ {
983
+ $and: [{ category: 'books' }, { $or: [{ price: { $lt: 30 } }, { rating: { $gt: 4.5 } }] }],
984
+ },
985
+ ],
986
+ },
817
987
  });
818
988
  expect(results.length).toBeGreaterThan(0);
819
989
  results.forEach(result => {
@@ -826,8 +996,12 @@ describe('ChromaVector Integration Tests', () => {
826
996
  });
827
997
 
828
998
  it('should handle multiple field comparisons with same value', async () => {
829
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
830
- $or: [{ price: { $gt: 500 } }, { rating: { $gt: 4.5 } }],
999
+ const results = await vectorDB.query({
1000
+ indexName: testIndexName2,
1001
+ queryVector: [1, 0, 0],
1002
+ filter: {
1003
+ $or: [{ price: { $gt: 500 } }, { rating: { $gt: 4.5 } }],
1004
+ },
831
1005
  });
832
1006
  expect(results.length).toBeGreaterThan(0);
833
1007
  results.forEach(result => {
@@ -838,12 +1012,16 @@ describe('ChromaVector Integration Tests', () => {
838
1012
 
839
1013
  describe('Performance Edge Cases', () => {
840
1014
  it('should handle filters with many conditions', async () => {
841
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
842
- $and: Array(10)
843
- .fill(null)
844
- .map(() => ({
845
- $or: [{ price: { $gt: 100 } }, { rating: { $gt: 4.0 } }],
846
- })),
1015
+ const results = await vectorDB.query({
1016
+ indexName: testIndexName2,
1017
+ queryVector: [1, 0, 0],
1018
+ filter: {
1019
+ $and: Array(10)
1020
+ .fill(null)
1021
+ .map(() => ({
1022
+ $or: [{ price: { $gt: 100 } }, { rating: { $gt: 4.0 } }],
1023
+ })),
1024
+ },
847
1025
  });
848
1026
  expect(results.length).toBeGreaterThan(0);
849
1027
  results.forEach(result => {
@@ -852,12 +1030,20 @@ describe('ChromaVector Integration Tests', () => {
852
1030
  });
853
1031
 
854
1032
  it('should handle deeply nested conditions efficiently', async () => {
855
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
856
- $or: Array(5)
857
- .fill(null)
858
- .map(() => ({
859
- $and: [{ category: { $in: ['electronics', 'books'] } }, { price: { $gt: 50 } }, { rating: { $gt: 4.0 } }],
860
- })),
1033
+ const results = await vectorDB.query({
1034
+ indexName: testIndexName2,
1035
+ queryVector: [1, 0, 0],
1036
+ filter: {
1037
+ $or: Array(5)
1038
+ .fill(null)
1039
+ .map(() => ({
1040
+ $and: [
1041
+ { category: { $in: ['electronics', 'books'] } },
1042
+ { price: { $gt: 50 } },
1043
+ { rating: { $gt: 4.0 } },
1044
+ ],
1045
+ })),
1046
+ },
861
1047
  });
862
1048
  expect(results.length).toBeGreaterThan(0);
863
1049
  results.forEach(result => {
@@ -868,22 +1054,331 @@ describe('ChromaVector Integration Tests', () => {
868
1054
  });
869
1055
 
870
1056
  it('should handle large number of $or conditions', async () => {
871
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
872
- $or: [
873
- ...Array(5)
874
- .fill(null)
875
- .map((_, i) => ({
876
- price: { $gt: i * 100 },
877
- })),
878
- ...Array(5)
879
- .fill(null)
880
- .map((_, i) => ({
881
- rating: { $gt: 4.0 + i * 0.1 },
882
- })),
883
- ],
1057
+ const results = await vectorDB.query({
1058
+ indexName: testIndexName2,
1059
+ queryVector: [1, 0, 0],
1060
+ filter: {
1061
+ $or: [
1062
+ ...Array(5)
1063
+ .fill(null)
1064
+ .map((_, i) => ({
1065
+ price: { $gt: i * 100 },
1066
+ })),
1067
+ ...Array(5)
1068
+ .fill(null)
1069
+ .map((_, i) => ({
1070
+ rating: { $gt: 4.0 + i * 0.1 },
1071
+ })),
1072
+ ],
1073
+ },
1074
+ });
1075
+ expect(results.length).toBeGreaterThan(0);
1076
+ });
1077
+ });
1078
+ });
1079
+
1080
+ describe('Document Operations and Filtering', () => {
1081
+ const testDocuments = [
1082
+ 'The quick brown fox jumps over the lazy dog',
1083
+ 'Pack my box with five dozen liquor jugs',
1084
+ 'How vexingly quick daft zebras JUMP',
1085
+ ];
1086
+
1087
+ beforeAll(async () => {
1088
+ try {
1089
+ await vectorDB.deleteIndex(testIndexName3);
1090
+ } catch {
1091
+ // Ignore errors if index doesn't exist
1092
+ }
1093
+ await vectorDB.createIndex({ indexName: testIndexName3, dimension });
1094
+
1095
+ const testVectors = [
1096
+ [1.0, 0.0, 0.0],
1097
+ [0.0, 1.0, 0.0],
1098
+ [0.0, 0.0, 1.0],
1099
+ ];
1100
+
1101
+ const testMetadata = [
1102
+ { source: 'pangram1', length: 43 },
1103
+ { source: 'pangram2', length: 32 },
1104
+ { source: 'pangram3', length: 30 },
1105
+ ];
1106
+ const testIds = ['doc1', 'doc2', 'doc3'];
1107
+
1108
+ await vectorDB.upsert({
1109
+ indexName: testIndexName3,
1110
+ vectors: testVectors,
1111
+ documents: testDocuments,
1112
+ metadata: testMetadata,
1113
+ ids: testIds,
1114
+ });
1115
+
1116
+ // Wait for indexing
1117
+ await new Promise(resolve => setTimeout(resolve, 2000));
1118
+ });
1119
+
1120
+ afterAll(async () => {
1121
+ // Cleanup after tests
1122
+ try {
1123
+ await vectorDB.deleteIndex(testIndexName3);
1124
+ } catch {
1125
+ // Ignore cleanup errors
1126
+ }
1127
+ });
1128
+
1129
+ describe('Basic Document Operations', () => {
1130
+ it('should store and retrieve documents', async () => {
1131
+ const results = await vectorDB.query({ indexName: testIndexName3, queryVector: [1.0, 0.0, 0.0], topK: 3 });
1132
+ expect(results).toHaveLength(3);
1133
+ // Verify documents are returned
1134
+ expect(results[0].document).toBe(testDocuments[0]);
1135
+ });
1136
+
1137
+ it('should filter documents using $contains', async () => {
1138
+ const results = await vectorDB.query({
1139
+ indexName: testIndexName3,
1140
+ queryVector: [1.0, 0.0, 0.0],
1141
+ topK: 3,
1142
+ documentFilter: { $contains: 'quick' },
1143
+ });
1144
+ expect(results).toHaveLength(2);
1145
+ });
1146
+
1147
+ it('should filter with $not_contains', async () => {
1148
+ const results = await vectorDB.query({
1149
+ indexName: testIndexName3,
1150
+ queryVector: [1.0, 0.0, 0.0],
1151
+ topK: 3,
1152
+ documentFilter: { $not_contains: 'fox' },
1153
+ });
1154
+ expect(results.every(r => !r.document?.includes('fox'))).toBe(true);
1155
+ });
1156
+
1157
+ it('should combine metadata and document filters', async () => {
1158
+ const results = await vectorDB.query({
1159
+ indexName: testIndexName3,
1160
+ queryVector: [1.0, 0.0, 0.0],
1161
+ topK: 3,
1162
+ filter: { source: 'pangram1' },
1163
+ documentFilter: { $contains: 'fox' },
1164
+ });
1165
+ expect(results).toHaveLength(1);
1166
+ expect(results[0].metadata?.source).toBe('pangram1');
1167
+ expect(results[0].document).toContain('fox');
1168
+ });
1169
+ });
1170
+
1171
+ describe('Complex Document Filtering', () => {
1172
+ it('should handle $and conditions', async () => {
1173
+ const results = await vectorDB.query({
1174
+ indexName: testIndexName3,
1175
+ queryVector: [1.0, 0.0, 0.0],
1176
+ topK: 3,
1177
+ documentFilter: { $and: [{ $contains: 'quick' }, { $not_contains: 'fox' }] },
1178
+ });
1179
+ expect(results).toHaveLength(1);
1180
+ expect(results[0].document).toContain('quick');
1181
+ expect(results[0].document).not.toContain('fox');
1182
+ });
1183
+
1184
+ it('should handle $or conditions', async () => {
1185
+ const results = await vectorDB.query({
1186
+ indexName: testIndexName3,
1187
+ queryVector: [1.0, 0.0, 0.0],
1188
+ topK: 3,
1189
+ documentFilter: { $or: [{ $contains: 'fox' }, { $contains: 'zebras' }] },
1190
+ });
1191
+ expect(results).toHaveLength(2);
1192
+ expect(results[0].document).toContain('fox');
1193
+ expect(results[1].document).toContain('zebras');
1194
+ });
1195
+ });
1196
+
1197
+ describe('Edge Cases and Validation', () => {
1198
+ it('should reject empty string in $contains', async () => {
1199
+ await expect(
1200
+ vectorDB.query({
1201
+ indexName: testIndexName3,
1202
+ queryVector: [1.0, 0.0, 0.0],
1203
+ topK: 3,
1204
+ documentFilter: { $contains: '' },
1205
+ }),
1206
+ ).rejects.toThrow('Expected where document operand value for operator $contains to be a non-empty str');
1207
+ });
1208
+
1209
+ it('should be case sensitive', async () => {
1210
+ // First verify lowercase works
1211
+ const lowerResults = await vectorDB.query({
1212
+ indexName: testIndexName3,
1213
+ queryVector: [1.0, 0.0, 0.0],
1214
+ topK: 3,
1215
+ documentFilter: { $contains: 'quick' },
1216
+ });
1217
+ expect(lowerResults.length).toBe(2);
1218
+
1219
+ // Then verify uppercase doesn't match
1220
+ const upperResults = await vectorDB.query({
1221
+ indexName: testIndexName3,
1222
+ queryVector: [1.0, 0.0, 0.0],
1223
+ topK: 3,
1224
+ documentFilter: { $contains: 'QUICK' },
1225
+ });
1226
+ expect(upperResults.length).toBe(0);
1227
+
1228
+ const upperResults2 = await vectorDB.query({
1229
+ indexName: testIndexName3,
1230
+ queryVector: [1.0, 0.0, 0.0],
1231
+ topK: 3,
1232
+ documentFilter: { $contains: 'JUMP' },
1233
+ });
1234
+ expect(upperResults2.length).toBe(1);
1235
+ });
1236
+
1237
+ it('should handle exact string matches', async () => {
1238
+ const results = await vectorDB.query({
1239
+ indexName: testIndexName3,
1240
+ queryVector: [1.0, 0.0, 0.0],
1241
+ topK: 3,
1242
+ documentFilter: { $contains: 'quick brown' }, // Test multi-word match
1243
+ });
1244
+ expect(results.length).toBe(1);
1245
+ expect(results[0].document).toContain('quick brown');
1246
+ });
1247
+
1248
+ it('should handle deeply nested logical operators', async () => {
1249
+ const results = await vectorDB.query({
1250
+ indexName: testIndexName3,
1251
+ queryVector: [1.0, 0.0, 0.0],
1252
+ topK: 3,
1253
+ documentFilter: {
1254
+ $or: [
1255
+ {
1256
+ $and: [{ $contains: 'quick' }, { $not_contains: 'fox' }],
1257
+ },
1258
+ {
1259
+ $and: [{ $contains: 'box' }, { $not_contains: 'quick' }],
1260
+ },
1261
+ ],
1262
+ },
884
1263
  });
885
1264
  expect(results.length).toBeGreaterThan(0);
1265
+ results.forEach(result => {
1266
+ if (result.document?.includes('quick')) {
1267
+ expect(result.document).not.toContain('fox');
1268
+ } else if (result.document?.includes('box')) {
1269
+ expect(result.document).not.toContain('quick');
1270
+ }
1271
+ });
1272
+ });
1273
+ });
1274
+ });
1275
+ describe('Deprecation Warnings', () => {
1276
+ const indexName = 'testdeprecationwarnings';
1277
+
1278
+ const indexName2 = 'testdeprecationwarnings2';
1279
+
1280
+ let warnSpy;
1281
+
1282
+ beforeAll(async () => {
1283
+ await vectorDB.createIndex({ indexName: indexName, dimension: 3 });
1284
+ });
1285
+
1286
+ afterAll(async () => {
1287
+ try {
1288
+ await vectorDB.deleteIndex(indexName);
1289
+ } catch {
1290
+ // Ignore errors if index doesn't exist
1291
+ }
1292
+ try {
1293
+ await vectorDB.deleteIndex(indexName2);
1294
+ } catch {
1295
+ // Ignore errors if index doesn't exist
1296
+ }
1297
+ });
1298
+
1299
+ beforeEach(async () => {
1300
+ warnSpy = vi.spyOn(vectorDB['logger'], 'warn');
1301
+ });
1302
+
1303
+ afterEach(async () => {
1304
+ warnSpy.mockRestore();
1305
+ try {
1306
+ await vectorDB.deleteIndex(indexName2);
1307
+ } catch {
1308
+ // Ignore errors if index doesn't exist
1309
+ }
1310
+ });
1311
+
1312
+ it('should show deprecation warning when using individual args for createIndex', async () => {
1313
+ await vectorDB.createIndex(indexName2, 3, 'cosine');
1314
+
1315
+ expect(warnSpy).toHaveBeenCalledWith(
1316
+ expect.stringContaining('Deprecation Warning: Passing individual arguments to createIndex() is deprecated'),
1317
+ );
1318
+ });
1319
+
1320
+ it('should show deprecation warning when using individual args for upsert', async () => {
1321
+ await vectorDB.upsert(indexName, [[1, 2, 3]], [{ test: 'data' }]);
1322
+
1323
+ expect(warnSpy).toHaveBeenCalledWith(
1324
+ expect.stringContaining('Deprecation Warning: Passing individual arguments to upsert() is deprecated'),
1325
+ );
1326
+ });
1327
+
1328
+ it('should show deprecation warning when using individual args for query', async () => {
1329
+ await vectorDB.query(indexName, [1, 2, 3], 5);
1330
+
1331
+ expect(warnSpy).toHaveBeenCalledWith(
1332
+ expect.stringContaining('Deprecation Warning: Passing individual arguments to query() is deprecated'),
1333
+ );
1334
+ });
1335
+
1336
+ it('should not show deprecation warning when using object param for query', async () => {
1337
+ await vectorDB.query({
1338
+ indexName,
1339
+ queryVector: [1, 2, 3],
1340
+ topK: 5,
1341
+ });
1342
+
1343
+ expect(warnSpy).not.toHaveBeenCalled();
1344
+ });
1345
+
1346
+ it('should not show deprecation warning when using object param for createIndex', async () => {
1347
+ await vectorDB.createIndex({
1348
+ indexName: indexName2,
1349
+ dimension: 3,
1350
+ metric: 'cosine',
1351
+ });
1352
+
1353
+ expect(warnSpy).not.toHaveBeenCalled();
1354
+ });
1355
+
1356
+ it('should not show deprecation warning when using object param for upsert', async () => {
1357
+ await vectorDB.upsert({
1358
+ indexName,
1359
+ vectors: [[1, 2, 3]],
1360
+ metadata: [{ test: 'data' }],
1361
+ });
1362
+
1363
+ expect(warnSpy).not.toHaveBeenCalled();
1364
+ });
1365
+
1366
+ it('should maintain backward compatibility with individual args', async () => {
1367
+ // Query
1368
+ const queryResults = await vectorDB.query(indexName, [1, 2, 3], 5);
1369
+ expect(Array.isArray(queryResults)).toBe(true);
1370
+
1371
+ // CreateIndex
1372
+ await expect(vectorDB.createIndex(indexName2, 3, 'cosine')).resolves.not.toThrow();
1373
+
1374
+ // Upsert
1375
+ const upsertResults = await vectorDB.upsert({
1376
+ indexName,
1377
+ vectors: [[1, 2, 3]],
1378
+ metadata: [{ test: 'data' }],
886
1379
  });
1380
+ expect(Array.isArray(upsertResults)).toBe(true);
1381
+ expect(upsertResults).toHaveLength(1);
887
1382
  });
888
1383
  });
889
1384
  });