@mastra/chroma 0.0.0-storage-20250225005900 → 0.0.0-trigger-playground-ui-package-20250506151043

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,18 +100,121 @@ 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
  });
120
+
121
+ it('should update the vector by id', async () => {
122
+ const ids = await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors });
123
+ expect(ids).toHaveLength(3);
124
+
125
+ const idToBeUpdated = ids[0];
126
+ const newVector = [1, 2, 3];
127
+ const newMetaData = {
128
+ test: 'updates',
129
+ };
130
+
131
+ const update = {
132
+ vector: newVector,
133
+ metadata: newMetaData,
134
+ };
135
+
136
+ await vectorDB.updateIndexById(testIndexName, idToBeUpdated, update);
137
+
138
+ const results: QueryResult[] = await vectorDB.query({
139
+ indexName: testIndexName,
140
+ queryVector: newVector,
141
+ topK: 2,
142
+ includeVector: true,
143
+ });
144
+ expect(results[0]?.id).toBe(idToBeUpdated);
145
+ expect(results[0]?.vector).toEqual(newVector);
146
+ expect(results[0]?.metadata).toEqual(newMetaData);
147
+ });
148
+
149
+ it('should only update the metadata by id', async () => {
150
+ const ids = await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors });
151
+ expect(ids).toHaveLength(3);
152
+
153
+ const idToBeUpdated = ids[0];
154
+ const newMetaData = {
155
+ test: 'updates',
156
+ };
157
+
158
+ const update = {
159
+ metadata: newMetaData,
160
+ };
161
+
162
+ await vectorDB.updateIndexById(testIndexName, idToBeUpdated, update);
163
+
164
+ const results: QueryResult[] = await vectorDB.query({
165
+ indexName: testIndexName,
166
+ queryVector: testVectors[0],
167
+ topK: 2,
168
+ includeVector: true,
169
+ });
170
+ expect(results[0]?.id).toBe(idToBeUpdated);
171
+ expect(results[0]?.vector).toEqual(testVectors[0]);
172
+ expect(results[0]?.metadata).toEqual(newMetaData);
173
+ });
174
+
175
+ it('should only update vector embeddings by id', async () => {
176
+ const ids = await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors });
177
+ expect(ids).toHaveLength(3);
178
+
179
+ const idToBeUpdated = ids[0];
180
+ const newVector = [1, 2, 3];
181
+
182
+ const update = {
183
+ vector: newVector,
184
+ };
185
+
186
+ await vectorDB.updateIndexById(testIndexName, idToBeUpdated, update);
187
+
188
+ const results: QueryResult[] = await vectorDB.query({
189
+ indexName: testIndexName,
190
+ queryVector: newVector,
191
+ topK: 2,
192
+ includeVector: true,
193
+ });
194
+ expect(results[0]?.id).toBe(idToBeUpdated);
195
+ expect(results[0]?.vector).toEqual(newVector);
196
+ });
197
+
198
+ it('should throw exception when no updates are given', async () => {
199
+ await expect(vectorDB.updateIndexById(testIndexName, 'id', {})).rejects.toThrow('No updates provided');
200
+ });
201
+
202
+ it('should delete the vector by id', async () => {
203
+ const ids = await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors });
204
+ expect(ids).toHaveLength(3);
205
+ const idToBeDeleted = ids[0];
206
+
207
+ await vectorDB.deleteIndexById(testIndexName, idToBeDeleted);
208
+
209
+ const results: QueryResult[] = await vectorDB.query({
210
+ indexName: testIndexName,
211
+ queryVector: [1.0, 0.0, 0.0],
212
+ topK: 2,
213
+ });
214
+
215
+ expect(results).toHaveLength(2);
216
+ expect(results.map(res => res.id)).not.toContain(idToBeDeleted);
217
+ });
114
218
  });
115
219
 
116
220
  describe('Query Operations', () => {
@@ -123,7 +227,7 @@ describe('ChromaVector Integration Tests', () => {
123
227
  const testIds = ['vec1', 'vec2', 'vec3'];
124
228
 
125
229
  beforeEach(async () => {
126
- await vectorDB.upsert(testIndexName, testVectors, testMetadata, testIds);
230
+ await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors, metadata: testMetadata, ids: testIds });
127
231
  });
128
232
 
129
233
  describe('Basic Queries', () => {
@@ -131,7 +235,7 @@ describe('ChromaVector Integration Tests', () => {
131
235
  const queryVector = [1.0, 0.1, 0.1];
132
236
  const topK = 2;
133
237
 
134
- const results: QueryResult[] = await vectorDB.query(testIndexName, queryVector, topK);
238
+ const results: QueryResult[] = await vectorDB.query({ indexName: testIndexName, queryVector, topK });
135
239
 
136
240
  expect(results).toHaveLength(topK);
137
241
  expect(results?.[0]?.id).toBe(testIds[0]); // Should match x-axis vector most closely
@@ -143,7 +247,7 @@ describe('ChromaVector Integration Tests', () => {
143
247
  const queryVector = [1.0, 1.0, 1.0];
144
248
  const filter = { label: 'x-axis' };
145
249
 
146
- const results = await vectorDB.query(testIndexName, queryVector, 3, filter);
250
+ const results = await vectorDB.query({ indexName: testIndexName, queryVector, topK: 3, filter });
147
251
 
148
252
  expect(results).toHaveLength(1);
149
253
  expect(results?.[0]?.metadata?.label).toBe('x-axis');
@@ -155,7 +259,12 @@ describe('ChromaVector Integration Tests', () => {
155
259
  const queryVector = [1.0, 0.1, 0.1];
156
260
  const topK = 1;
157
261
 
158
- const results = await vectorDB.query(testIndexName, queryVector, topK, undefined, true);
262
+ const results = await vectorDB.query({
263
+ indexName: testIndexName,
264
+ queryVector,
265
+ topK,
266
+ includeVector: true,
267
+ });
159
268
 
160
269
  expect(results).toHaveLength(topK);
161
270
  expect(results?.[0]?.vector).toEqual(testVectors[0]);
@@ -165,73 +274,95 @@ describe('ChromaVector Integration Tests', () => {
165
274
 
166
275
  describe('Error Handling', () => {
167
276
  it('should handle non-existent index queries', async () => {
168
- await expect(vectorDB.query('non-existent-index-yu', [1, 2, 3])).rejects.toThrow();
277
+ await expect(vectorDB.query({ indexName: 'non-existent-index-yu', queryVector: [1, 2, 3] })).rejects.toThrow();
169
278
  });
170
279
 
171
280
  it('should handle invalid dimension vectors', async () => {
172
281
  const invalidVector = [1, 2, 3, 4]; // 4D vector for 3D index
173
- await expect(vectorDB.upsert(testIndexName, [invalidVector])).rejects.toThrow();
282
+ await expect(vectorDB.upsert({ indexName: testIndexName, vectors: [invalidVector] })).rejects.toThrow();
174
283
  });
175
284
 
176
285
  it('should handle mismatched metadata and vectors length', async () => {
177
286
  const vectors = [[1, 2, 3]];
178
287
  const metadata = [{}, {}]; // More metadata than vectors
179
- await expect(vectorDB.upsert(testIndexName, vectors, metadata)).rejects.toThrow();
288
+ await expect(vectorDB.upsert({ indexName: testIndexName, vectors, metadata })).rejects.toThrow();
180
289
  });
181
290
  });
182
291
 
183
292
  describe('Filter Validation in Queries', () => {
184
293
  it('rejects queries with null values', async () => {
185
294
  await expect(
186
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
187
- field: null,
295
+ vectorDB.query({
296
+ indexName: testIndexName,
297
+ queryVector: [1, 0, 0],
298
+ filter: {
299
+ field: null,
300
+ },
188
301
  }),
189
302
  ).rejects.toThrow();
190
303
 
191
304
  await expect(
192
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
193
- other: { $eq: null },
305
+ vectorDB.query({
306
+ indexName: testIndexName,
307
+ queryVector: [1, 0, 0],
308
+ filter: {
309
+ other: { $eq: null },
310
+ },
194
311
  }),
195
312
  ).rejects.toThrow();
196
313
  });
197
314
 
198
315
  it('validates array operator values', async () => {
199
316
  await expect(
200
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
201
- tags: { $in: null },
317
+ vectorDB.query({
318
+ indexName: testIndexName,
319
+ queryVector: [1, 0, 0],
320
+ filter: {
321
+ tags: { $in: null },
322
+ },
202
323
  }),
203
324
  ).rejects.toThrow();
204
325
  });
205
326
 
206
327
  it('validates numeric values for comparison operators', async () => {
207
328
  await expect(
208
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
209
- price: { $gt: 'not-a-number' },
329
+ vectorDB.query({
330
+ indexName: testIndexName,
331
+ queryVector: [1, 0, 0],
332
+ filter: {
333
+ price: { $gt: 'not-a-number' },
334
+ },
210
335
  }),
211
336
  ).rejects.toThrow();
212
337
  });
213
338
 
214
339
  it('validates value types', async () => {
215
340
  await expect(
216
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
217
- date: { $gt: 'not-a-date' },
341
+ vectorDB.query({
342
+ indexName: testIndexName,
343
+ queryVector: [1, 0, 0],
344
+ filter: { date: { $gt: 'not-a-date' } },
218
345
  }),
219
346
  ).rejects.toThrow();
220
347
 
221
348
  await expect(
222
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
223
- number: { $lt: 'not-a-number' },
349
+ vectorDB.query({
350
+ indexName: testIndexName,
351
+ queryVector: [1, 0, 0],
352
+ filter: { number: { $lt: 'not-a-number' } },
224
353
  }),
225
354
  ).rejects.toThrow();
226
355
  });
227
356
 
228
357
  it('validates array operators', async () => {
229
- const invalidValues = [123, 'string', true, { key: 'value' }, null, undefined];
358
+ const invalidValues = [123, 'string', true, { key: 'value' }, null];
230
359
  for (const op of ['$in', '$nin']) {
231
360
  for (const val of invalidValues) {
232
361
  await expect(
233
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
234
- field: { [op]: val },
362
+ vectorDB.query({
363
+ indexName: testIndexName,
364
+ queryVector: [1, 0, 0],
365
+ filter: { field: { [op]: val } },
235
366
  }),
236
367
  ).rejects.toThrow();
237
368
  }
@@ -244,8 +375,10 @@ describe('ChromaVector Integration Tests', () => {
244
375
  for (const op of ['$in', '$nin']) {
245
376
  for (const val of invalidValues) {
246
377
  await expect(
247
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
248
- field: { [op]: val },
378
+ vectorDB.query({
379
+ indexName: testIndexName,
380
+ queryVector: [1, 0, 0],
381
+ filter: { field: { [op]: val } },
249
382
  }),
250
383
  ).rejects.toThrow();
251
384
  }
@@ -256,8 +389,10 @@ describe('ChromaVector Integration Tests', () => {
256
389
  // Basic equality can accept any non-undefined value
257
390
  for (const op of ['$eq', '$ne']) {
258
391
  await expect(
259
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
260
- field: { [op]: undefined },
392
+ vectorDB.query({
393
+ indexName: testIndexName,
394
+ queryVector: [1, 0, 0],
395
+ filter: { field: { [op]: undefined } },
261
396
  }),
262
397
  ).rejects.toThrow();
263
398
  }
@@ -268,8 +403,10 @@ describe('ChromaVector Integration Tests', () => {
268
403
  for (const op of numOps) {
269
404
  for (const val of invalidNumericValues) {
270
405
  await expect(
271
- vectorDB.query(testIndexName, [1, 0, 0], 10, {
272
- field: { [op]: val },
406
+ vectorDB.query({
407
+ indexName: testIndexName,
408
+ queryVector: [1, 0, 0],
409
+ filter: { field: { [op]: val } },
273
410
  }),
274
411
  ).rejects.toThrow();
275
412
  }
@@ -278,29 +415,45 @@ describe('ChromaVector Integration Tests', () => {
278
415
 
279
416
  it('validates multiple invalid values', async () => {
280
417
  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' },
418
+ vectorDB.query({
419
+ indexName: testIndexName,
420
+ queryVector: [1, 0, 0],
421
+ filter: {
422
+ field1: { $in: 'not-array' },
423
+ field2: { $exists: 'not-boolean' },
424
+ field3: { $gt: 'not-number' },
425
+ },
285
426
  }),
286
427
  ).rejects.toThrow();
287
428
  });
288
429
 
289
430
  it('handles empty object filters', async () => {
290
431
  // Test empty object at top level
291
- await expect(vectorDB.query(testIndexName, [1, 0, 0], 10, { field: { $eq: {} } })).rejects.toThrow();
432
+ await expect(
433
+ vectorDB.query({
434
+ indexName: testIndexName,
435
+ queryVector: [1, 0, 0],
436
+ filter: { field: { $eq: {} } },
437
+ }),
438
+ ).rejects.toThrow();
292
439
  });
293
440
 
294
441
  it('handles empty/undefined filters by returning all results', async () => {
295
442
  const noFilterCases = [{ field: {} }, { field: undefined }, { field: { $in: undefined } }];
296
443
 
297
444
  for (const filter of noFilterCases) {
298
- await expect(vectorDB.query(testIndexName, [1, 0, 0], 10, filter)).rejects.toThrow();
445
+ await expect(vectorDB.query({ indexName: testIndexName, queryVector: [1, 0, 0], filter })).rejects.toThrow();
299
446
  }
300
447
  });
301
448
  it('handles empty object filters', async () => {
302
449
  // Test empty object at top level
303
- await expect(vectorDB.query(testIndexName, [1, 0, 0], 10, {})).rejects.toThrow();
450
+ await expect(
451
+ vectorDB.query({
452
+ indexName: testIndexName,
453
+ queryVector: [1, 0, 0],
454
+ filter: {},
455
+ }),
456
+ ).rejects.toThrow();
304
457
  });
305
458
  });
306
459
 
@@ -309,10 +462,10 @@ describe('ChromaVector Integration Tests', () => {
309
462
  beforeAll(async () => {
310
463
  try {
311
464
  await vectorDB.deleteIndex(testIndexName2);
312
- } catch (error) {
465
+ } catch {
313
466
  // Ignore errors if index doesn't exist
314
467
  }
315
- await vectorDB.createIndex(testIndexName2, dimension);
468
+ await vectorDB.createIndex({ indexName: testIndexName2, dimension });
316
469
 
317
470
  const vectors = [
318
471
  [1, 0, 0], // Electronics
@@ -348,7 +501,7 @@ describe('ChromaVector Integration Tests', () => {
348
501
  },
349
502
  ];
350
503
 
351
- await vectorDB.upsert(testIndexName2, vectors, metadata);
504
+ await vectorDB.upsert({ indexName: testIndexName2, vectors, metadata });
352
505
  // Wait for indexing
353
506
  await new Promise(resolve => setTimeout(resolve, 2000));
354
507
  });
@@ -357,15 +510,17 @@ describe('ChromaVector Integration Tests', () => {
357
510
  // Cleanup after tests
358
511
  try {
359
512
  await vectorDB.deleteIndex(testIndexName2);
360
- } catch (error) {
513
+ } catch {
361
514
  // Ignore cleanup errors
362
515
  }
363
516
  });
364
517
 
365
518
  describe('Basic Comparison Operators', () => {
366
519
  it('filters with $eq operator', async () => {
367
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
368
- category: { $eq: 'electronics' },
520
+ const results = await vectorDB.query({
521
+ indexName: testIndexName2,
522
+ queryVector: [1, 0, 0],
523
+ filter: { category: { $eq: 'electronics' } },
369
524
  });
370
525
  expect(results.length).toBe(2);
371
526
  results.forEach(result => {
@@ -374,8 +529,10 @@ describe('ChromaVector Integration Tests', () => {
374
529
  });
375
530
 
376
531
  it('filters with implicit $eq', async () => {
377
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
378
- category: 'electronics', // implicit $eq
532
+ const results = await vectorDB.query({
533
+ indexName: testIndexName2,
534
+ queryVector: [1, 0, 0],
535
+ filter: { category: 'electronics' }, // implicit $eq
379
536
  });
380
537
  expect(results.length).toBe(2);
381
538
  results.forEach(result => {
@@ -383,8 +540,10 @@ describe('ChromaVector Integration Tests', () => {
383
540
  });
384
541
  });
385
542
  it('filters with $gt operator', async () => {
386
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
387
- price: { $gt: 500 },
543
+ const results = await vectorDB.query({
544
+ indexName: testIndexName2,
545
+ queryVector: [1, 0, 0],
546
+ filter: { price: { $gt: 500 } },
388
547
  });
389
548
  expect(results.length).toBe(1);
390
549
  results.forEach(result => {
@@ -393,8 +552,10 @@ describe('ChromaVector Integration Tests', () => {
393
552
  });
394
553
 
395
554
  it('filters with $gte operator', async () => {
396
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
397
- price: { $gte: 500 },
555
+ const results = await vectorDB.query({
556
+ indexName: testIndexName2,
557
+ queryVector: [1, 0, 0],
558
+ filter: { price: { $gte: 500 } },
398
559
  });
399
560
  expect(results.length).toBe(2);
400
561
  results.forEach(result => {
@@ -403,8 +564,10 @@ describe('ChromaVector Integration Tests', () => {
403
564
  });
404
565
 
405
566
  it('filters with $lt operator', async () => {
406
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
407
- price: { $lt: 100 },
567
+ const results = await vectorDB.query({
568
+ indexName: testIndexName2,
569
+ queryVector: [1, 0, 0],
570
+ filter: { price: { $lt: 100 } },
408
571
  });
409
572
  expect(results.length).toBe(2);
410
573
  results.forEach(result => {
@@ -413,8 +576,10 @@ describe('ChromaVector Integration Tests', () => {
413
576
  });
414
577
 
415
578
  it('filters with $lte operator', async () => {
416
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
417
- price: { $lte: 500 },
579
+ const results = await vectorDB.query({
580
+ indexName: testIndexName2,
581
+ queryVector: [1, 0, 0],
582
+ filter: { price: { $lte: 500 } },
418
583
  });
419
584
  expect(results.length).toBeGreaterThan(0);
420
585
  results.forEach(result => {
@@ -423,8 +588,10 @@ describe('ChromaVector Integration Tests', () => {
423
588
  });
424
589
 
425
590
  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 },
591
+ const results = await vectorDB.query({
592
+ indexName: testIndexName2,
593
+ queryVector: [1, 0, 0],
594
+ filter: { price: { $gte: 25, $lte: 500 } },
428
595
  });
429
596
  expect(results.length).toBe(2);
430
597
  results.forEach(result => {
@@ -434,8 +601,10 @@ describe('ChromaVector Integration Tests', () => {
434
601
  });
435
602
 
436
603
  it('filters with $ne operator', async () => {
437
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
438
- category: { $ne: 'electronics' },
604
+ const results = await vectorDB.query({
605
+ indexName: testIndexName2,
606
+ queryVector: [1, 0, 0],
607
+ filter: { category: { $ne: 'electronics' } },
439
608
  });
440
609
  expect(results.length).toBe(2);
441
610
  results.forEach(result => {
@@ -444,8 +613,10 @@ describe('ChromaVector Integration Tests', () => {
444
613
  });
445
614
 
446
615
  it('filters with boolean values', async () => {
447
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
448
- inStock: true, // test both implicit
616
+ const results = await vectorDB.query({
617
+ indexName: testIndexName2,
618
+ queryVector: [1, 0, 0],
619
+ filter: { inStock: true }, // test both implicit
449
620
  });
450
621
  expect(results.length).toBe(3);
451
622
  results.forEach(result => {
@@ -454,9 +625,10 @@ describe('ChromaVector Integration Tests', () => {
454
625
  });
455
626
 
456
627
  it('filters with multiple fields', async () => {
457
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
458
- category: 'electronics',
459
- price: 1000,
628
+ const results = await vectorDB.query({
629
+ indexName: testIndexName2,
630
+ queryVector: [1, 0, 0],
631
+ filter: { category: 'electronics', price: 1000 },
460
632
  });
461
633
  expect(results.length).toBeGreaterThan(0);
462
634
  results.forEach(result => {
@@ -467,8 +639,10 @@ describe('ChromaVector Integration Tests', () => {
467
639
 
468
640
  describe('Array Operators', () => {
469
641
  it('filters with $in operator', async () => {
470
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
471
- category: { $in: ['electronics', 'books'] },
642
+ const results = await vectorDB.query({
643
+ indexName: testIndexName2,
644
+ queryVector: [1, 0, 0],
645
+ filter: { category: { $in: ['electronics', 'books'] } },
472
646
  });
473
647
  expect(results.length).toBeGreaterThan(0);
474
648
  results.forEach(result => {
@@ -477,8 +651,10 @@ describe('ChromaVector Integration Tests', () => {
477
651
  });
478
652
 
479
653
  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] },
654
+ const results = await vectorDB.query({
655
+ indexName: testIndexName2,
656
+ queryVector: [1, 0, 0],
657
+ filter: { price: { $in: [50, 75, 1000] } },
482
658
  });
483
659
  expect(results.length).toBeGreaterThan(0);
484
660
  results.forEach(result => {
@@ -487,8 +663,10 @@ describe('ChromaVector Integration Tests', () => {
487
663
  });
488
664
 
489
665
  it('filters with $in operator for booleans', async () => {
490
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
491
- inStock: { $in: [true] },
666
+ const results = await vectorDB.query({
667
+ indexName: testIndexName2,
668
+ queryVector: [1, 0, 0],
669
+ filter: { inStock: { $in: [true] } },
492
670
  });
493
671
  expect(results.length).toBeGreaterThan(0);
494
672
  results.forEach(result => {
@@ -499,8 +677,10 @@ describe('ChromaVector Integration Tests', () => {
499
677
 
500
678
  describe('Logical Operators', () => {
501
679
  it('filters with $and operator', async () => {
502
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
503
- $and: [{ category: 'electronics' }, { price: { $gt: 500 } }],
680
+ const results = await vectorDB.query({
681
+ indexName: testIndexName2,
682
+ queryVector: [1, 0, 0],
683
+ filter: { $and: [{ category: 'electronics' }, { price: { $gt: 500 } }] },
504
684
  });
505
685
  expect(results.length).toBe(1);
506
686
  expect(results[0]?.metadata?.category).toBe('electronics');
@@ -508,8 +688,10 @@ describe('ChromaVector Integration Tests', () => {
508
688
  });
509
689
 
510
690
  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 }],
691
+ const results = await vectorDB.query({
692
+ indexName: testIndexName2,
693
+ queryVector: [1, 0, 0],
694
+ filter: { $and: [{ category: 'electronics' }, { price: { $gt: 700 } }, { inStock: true }] },
513
695
  });
514
696
  expect(results.length).toBeGreaterThan(0);
515
697
  results.forEach(result => {
@@ -520,8 +702,10 @@ describe('ChromaVector Integration Tests', () => {
520
702
  });
521
703
 
522
704
  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 } }],
705
+ const results = await vectorDB.query({
706
+ indexName: testIndexName2,
707
+ queryVector: [1, 0, 0],
708
+ filter: { $or: [{ price: { $gt: 900 } }, { rating: { $gt: 4.8 } }] },
525
709
  });
526
710
  expect(results.length).toBe(2);
527
711
  results.forEach(result => {
@@ -530,8 +714,10 @@ describe('ChromaVector Integration Tests', () => {
530
714
  });
531
715
 
532
716
  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'] } }],
717
+ const results = await vectorDB.query({
718
+ indexName: testIndexName2,
719
+ queryVector: [1, 0, 0],
720
+ filter: { $or: [{ price: { $gt: 900 } }, { category: { $in: ['electronics', 'books'] } }] },
535
721
  });
536
722
  expect(results.length).toBeGreaterThan(0);
537
723
  results.forEach(result => {
@@ -542,14 +728,18 @@ describe('ChromaVector Integration Tests', () => {
542
728
  });
543
729
 
544
730
  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
- ],
731
+ const results = await vectorDB.query({
732
+ indexName: testIndexName2,
733
+ queryVector: [1, 0, 0],
734
+ filter: {
735
+ $and: [
736
+ {
737
+ $or: [{ category: 'electronics' }, { category: 'books' }],
738
+ },
739
+ { price: { $lt: 100 } },
740
+ { inStock: true },
741
+ ],
742
+ },
553
743
  });
554
744
  expect(results.length).toBeGreaterThan(0);
555
745
  results.forEach(result => {
@@ -560,37 +750,45 @@ describe('ChromaVector Integration Tests', () => {
560
750
  });
561
751
 
562
752
  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 } }],
753
+ const results = await vectorDB.query({
754
+ indexName: testIndexName2,
755
+ queryVector: [1, 0, 0],
756
+ filter: { $or: [{ category: 'electronics' }, { price: { $gt: 100 } }] },
565
757
  });
566
758
  expect(results.length).toBeGreaterThan(0);
567
759
  });
568
760
 
569
- it('requires multiple conditions in logical operators', async () => {
570
- await expect(
571
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
572
- $and: [{ category: 'electronics' }],
573
- }),
574
- ).rejects.toThrow();
761
+ it('accepts single conditions in logical operators', async () => {
762
+ const results = await vectorDB.query({
763
+ indexName: testIndexName2,
764
+ queryVector: [1, 0, 0],
765
+ filter: { $and: [{ category: 'electronics' }] },
766
+ });
767
+ expect(results.length).toBeGreaterThan(0);
575
768
 
576
- await expect(
577
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
578
- $or: [{ price: { $gt: 900 } }],
579
- }),
580
- ).rejects.toThrow();
769
+ const results2 = await vectorDB.query({
770
+ indexName: testIndexName2,
771
+ queryVector: [1, 0, 0],
772
+ filter: { $or: [{ price: { $gt: 900 } }] },
773
+ });
774
+ expect(results2.length).toBeGreaterThan(0);
581
775
  });
582
776
  });
583
777
 
584
778
  describe('Complex Filter Combinations', () => {
585
779
  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
- ],
780
+ const results = await vectorDB.query({
781
+ indexName: testIndexName2,
782
+ queryVector: [1, 0, 0],
783
+ filter: {
784
+ $and: [
785
+ { price: { $gt: 20 } },
786
+ { inStock: true },
787
+ {
788
+ $or: [{ category: { $in: ['books'] } }, { rating: { $gt: 4.5 } }],
789
+ },
790
+ ],
791
+ },
594
792
  });
595
793
  expect(results.length).toBeGreaterThan(0);
596
794
  results.forEach(result => {
@@ -601,15 +799,19 @@ describe('ChromaVector Integration Tests', () => {
601
799
  });
602
800
 
603
801
  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
- ],
802
+ const results = await vectorDB.query({
803
+ indexName: testIndexName2,
804
+ queryVector: [1, 0, 0],
805
+ filter: {
806
+ $or: [
807
+ {
808
+ $and: [{ category: 'electronics' }, { price: { $gt: 700 } }],
809
+ },
810
+ {
811
+ $and: [{ category: 'books' }, { price: { $lt: 20 } }],
812
+ },
813
+ ],
814
+ },
613
815
  });
614
816
  expect(results.length).toBeGreaterThan(0);
615
817
  results.forEach(result => {
@@ -622,8 +824,10 @@ describe('ChromaVector Integration Tests', () => {
622
824
  });
623
825
 
624
826
  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 } }],
827
+ const results = await vectorDB.query({
828
+ indexName: testIndexName2,
829
+ queryVector: [1, 0, 0],
830
+ filter: { $and: [{ price: { $gte: 500 } }, { rating: { $gt: 4.5 } }] },
627
831
  });
628
832
  expect(results.length).toBeGreaterThan(0);
629
833
  results.forEach(result => {
@@ -633,8 +837,10 @@ describe('ChromaVector Integration Tests', () => {
633
837
  });
634
838
 
635
839
  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 } }],
840
+ const results = await vectorDB.query({
841
+ indexName: testIndexName2,
842
+ queryVector: [1, 0, 0],
843
+ filter: { $and: [{ price: { $gte: 30 } }, { price: { $lte: 800 } }] },
638
844
  });
639
845
  expect(results.length).toBeGreaterThan(0);
640
846
  results.forEach(result => {
@@ -645,15 +851,19 @@ describe('ChromaVector Integration Tests', () => {
645
851
  });
646
852
 
647
853
  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
- ],
854
+ const results = await vectorDB.query({
855
+ indexName: testIndexName2,
856
+ queryVector: [1, 0, 0],
857
+ filter: {
858
+ $or: [
859
+ {
860
+ $and: [{ category: 'electronics' }, { price: { $gt: 700 } }, { rating: { $gt: 4.5 } }],
861
+ },
862
+ {
863
+ $and: [{ category: 'books' }, { price: { $lt: 50 } }, { rating: { $gt: 4.0 } }],
864
+ },
865
+ ],
866
+ },
657
867
  });
658
868
  expect(results.length).toBeGreaterThan(0);
659
869
  results.forEach(result => {
@@ -701,13 +911,19 @@ describe('ChromaVector Integration Tests', () => {
701
911
  },
702
912
  ];
703
913
 
704
- await vectorDB.upsert(testIndexName2, vectors, metadata);
914
+ await vectorDB.upsert({
915
+ indexName: testIndexName2,
916
+ vectors,
917
+ metadata,
918
+ });
705
919
  await new Promise(resolve => setTimeout(resolve, 2000));
706
920
  });
707
921
 
708
922
  it('handles special numeric values', async () => {
709
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
710
- $or: [{ zero: 0 }, { negativeZero: 0 }],
923
+ const results = await vectorDB.query({
924
+ indexName: testIndexName2,
925
+ queryVector: [1, 0, 0],
926
+ filter: { $or: [{ zero: 0 }, { negativeZero: 0 }] },
711
927
  });
712
928
  expect(results.length).toBeGreaterThan(0);
713
929
  results.forEach(result => {
@@ -717,15 +933,23 @@ describe('ChromaVector Integration Tests', () => {
717
933
  });
718
934
 
719
935
  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 } }],
936
+ const results = await vectorDB.query({
937
+ indexName: testIndexName2,
938
+ queryVector: [1, 0, 0],
939
+ filter: {
940
+ $or: [{ maxInt: { $gte: Number.MAX_SAFE_INTEGER } }, { minInt: { $lte: Number.MIN_SAFE_INTEGER } }],
941
+ },
722
942
  });
723
943
  expect(results.length).toBe(1);
724
944
  });
725
945
 
726
946
  it('should handle numeric comparisons with decimals', async () => {
727
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
728
- rating: { $gt: 4.5 },
947
+ const results = await vectorDB.query({
948
+ indexName: testIndexName2,
949
+ queryVector: [1, 0, 0],
950
+ filter: {
951
+ rating: { $gt: 4.5 },
952
+ },
729
953
  });
730
954
  expect(results.length).toBeGreaterThan(0);
731
955
  results.forEach(result => {
@@ -734,8 +958,10 @@ describe('ChromaVector Integration Tests', () => {
734
958
  });
735
959
 
736
960
  it('should handle boolean values', async () => {
737
- const results = await vectorDB.query(testIndexName2, [1, 0, 0], 10, {
738
- inStock: { $eq: false },
961
+ const results = await vectorDB.query({
962
+ indexName: testIndexName2,
963
+ queryVector: [1, 0, 0],
964
+ filter: { inStock: { $eq: false } },
739
965
  });
740
966
  expect(results.length).toBeGreaterThan(0);
741
967
  results.forEach(result => {
@@ -747,46 +973,76 @@ describe('ChromaVector Integration Tests', () => {
747
973
  describe('Additional Validation Tests', () => {
748
974
  it('should throw error as date is not supported', async () => {
749
975
  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
- ],
976
+ vectorDB.query({
977
+ indexName: testIndexName2,
978
+ queryVector: [1, 0, 0],
979
+ filter: {
980
+ $and: [
981
+ { currentDate: { $lte: new Date().toISOString() } },
982
+ { currentDate: { $gt: new Date(0).toISOString() } },
983
+ ],
984
+ },
755
985
  }),
756
986
  ).rejects.toThrow();
757
987
  });
758
988
  it('should throw error as empty array in $in operator is not supported', async () => {
759
989
  await expect(
760
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
761
- category: { $in: [] },
990
+ vectorDB.query({
991
+ indexName: testIndexName2,
992
+ queryVector: [1, 0, 0],
993
+ filter: {
994
+ category: { $in: [] },
995
+ },
762
996
  }),
763
997
  ).rejects.toThrow();
764
998
  });
765
999
  it('should reject non-numeric values in numeric comparisons', async () => {
766
1000
  await expect(
767
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
768
- price: { $gt: '500' }, // string instead of number
1001
+ vectorDB.query({
1002
+ indexName: testIndexName2,
1003
+ queryVector: [1, 0, 0],
1004
+ filter: {
1005
+ price: { $gt: '500' }, // string instead of number
1006
+ },
769
1007
  }),
770
1008
  ).rejects.toThrow();
771
1009
  });
772
1010
 
773
1011
  it('should reject mixed types in $in operator', async () => {
774
1012
  await expect(
775
- vectorDB.query(testIndexName2, [1, 0, 0], 10, {
776
- field: { $in: ['string', 123] }, // mixed string and number
1013
+ vectorDB.query({
1014
+ indexName: testIndexName2,
1015
+ queryVector: [1, 0, 0],
1016
+ filter: {
1017
+ field: { $in: ['string', 123] }, // mixed string and number
1018
+ },
777
1019
  }),
778
1020
  ).rejects.toThrow();
779
1021
  });
780
1022
  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);
1023
+ const results1 = await vectorDB.query({
1024
+ indexName: testIndexName2,
1025
+ queryVector: [1, 0, 0],
1026
+ filter: undefined,
1027
+ });
1028
+ const results2 = await vectorDB.query({
1029
+ indexName: testIndexName2,
1030
+ queryVector: [1, 0, 0],
1031
+ });
783
1032
  expect(results1).toEqual(results2);
784
1033
  expect(results1.length).toBeGreaterThan(0);
785
1034
  });
786
1035
 
787
1036
  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);
1037
+ const results = await vectorDB.query({
1038
+ indexName: testIndexName2,
1039
+ queryVector: [1, 0, 0],
1040
+ filter: null,
1041
+ });
1042
+ const results2 = await vectorDB.query({
1043
+ indexName: testIndexName2,
1044
+ queryVector: [1, 0, 0],
1045
+ });
790
1046
  expect(results).toEqual(results2);
791
1047
  expect(results.length).toBeGreaterThan(0);
792
1048
  });
@@ -794,8 +1050,12 @@ describe('ChromaVector Integration Tests', () => {
794
1050
 
795
1051
  describe('Additional Edge Cases', () => {
796
1052
  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 } }],
1053
+ const results = await vectorDB.query({
1054
+ indexName: testIndexName2,
1055
+ queryVector: [1, 0, 0],
1056
+ filter: {
1057
+ $and: [{ price: { $gte: 25 } }, { price: { $lte: 1000 } }],
1058
+ },
799
1059
  });
800
1060
  expect(results.length).toBeGreaterThan(0);
801
1061
  expect(results.some(r => r.metadata?.price === 25)).toBe(true);
@@ -805,15 +1065,19 @@ describe('ChromaVector Integration Tests', () => {
805
1065
 
806
1066
  describe('Additional Complex Logical Combinations', () => {
807
1067
  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
- ],
1068
+ const results = await vectorDB.query({
1069
+ indexName: testIndexName2,
1070
+ queryVector: [1, 0, 0],
1071
+ filter: {
1072
+ $or: [
1073
+ {
1074
+ $and: [{ category: 'electronics' }, { $or: [{ price: { $gt: 900 } }, { rating: { $gt: 4.8 } }] }],
1075
+ },
1076
+ {
1077
+ $and: [{ category: 'books' }, { $or: [{ price: { $lt: 30 } }, { rating: { $gt: 4.5 } }] }],
1078
+ },
1079
+ ],
1080
+ },
817
1081
  });
818
1082
  expect(results.length).toBeGreaterThan(0);
819
1083
  results.forEach(result => {
@@ -826,8 +1090,12 @@ describe('ChromaVector Integration Tests', () => {
826
1090
  });
827
1091
 
828
1092
  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 } }],
1093
+ const results = await vectorDB.query({
1094
+ indexName: testIndexName2,
1095
+ queryVector: [1, 0, 0],
1096
+ filter: {
1097
+ $or: [{ price: { $gt: 500 } }, { rating: { $gt: 4.5 } }],
1098
+ },
831
1099
  });
832
1100
  expect(results.length).toBeGreaterThan(0);
833
1101
  results.forEach(result => {
@@ -838,12 +1106,16 @@ describe('ChromaVector Integration Tests', () => {
838
1106
 
839
1107
  describe('Performance Edge Cases', () => {
840
1108
  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
- })),
1109
+ const results = await vectorDB.query({
1110
+ indexName: testIndexName2,
1111
+ queryVector: [1, 0, 0],
1112
+ filter: {
1113
+ $and: Array(10)
1114
+ .fill(null)
1115
+ .map(() => ({
1116
+ $or: [{ price: { $gt: 100 } }, { rating: { $gt: 4.0 } }],
1117
+ })),
1118
+ },
847
1119
  });
848
1120
  expect(results.length).toBeGreaterThan(0);
849
1121
  results.forEach(result => {
@@ -852,12 +1124,20 @@ describe('ChromaVector Integration Tests', () => {
852
1124
  });
853
1125
 
854
1126
  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
- })),
1127
+ const results = await vectorDB.query({
1128
+ indexName: testIndexName2,
1129
+ queryVector: [1, 0, 0],
1130
+ filter: {
1131
+ $or: Array(5)
1132
+ .fill(null)
1133
+ .map(() => ({
1134
+ $and: [
1135
+ { category: { $in: ['electronics', 'books'] } },
1136
+ { price: { $gt: 50 } },
1137
+ { rating: { $gt: 4.0 } },
1138
+ ],
1139
+ })),
1140
+ },
861
1141
  });
862
1142
  expect(results.length).toBeGreaterThan(0);
863
1143
  results.forEach(result => {
@@ -868,22 +1148,452 @@ describe('ChromaVector Integration Tests', () => {
868
1148
  });
869
1149
 
870
1150
  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
- ],
1151
+ const results = await vectorDB.query({
1152
+ indexName: testIndexName2,
1153
+ queryVector: [1, 0, 0],
1154
+ filter: {
1155
+ $or: [
1156
+ ...Array(5)
1157
+ .fill(null)
1158
+ .map((_, i) => ({
1159
+ price: { $gt: i * 100 },
1160
+ })),
1161
+ ...Array(5)
1162
+ .fill(null)
1163
+ .map((_, i) => ({
1164
+ rating: { $gt: 4.0 + i * 0.1 },
1165
+ })),
1166
+ ],
1167
+ },
1168
+ });
1169
+ expect(results.length).toBeGreaterThan(0);
1170
+ });
1171
+ });
1172
+ });
1173
+
1174
+ describe('Document Operations and Filtering', () => {
1175
+ const testDocuments = [
1176
+ 'The quick brown fox jumps over the lazy dog',
1177
+ 'Pack my box with five dozen liquor jugs',
1178
+ 'How vexingly quick daft zebras JUMP',
1179
+ ];
1180
+
1181
+ beforeAll(async () => {
1182
+ try {
1183
+ await vectorDB.deleteIndex(testIndexName3);
1184
+ } catch {
1185
+ // Ignore errors if index doesn't exist
1186
+ }
1187
+ await vectorDB.createIndex({ indexName: testIndexName3, dimension });
1188
+
1189
+ const testVectors = [
1190
+ [1.0, 0.0, 0.0],
1191
+ [0.0, 1.0, 0.0],
1192
+ [0.0, 0.0, 1.0],
1193
+ ];
1194
+
1195
+ const testMetadata = [
1196
+ { source: 'pangram1', length: 43 },
1197
+ { source: 'pangram2', length: 32 },
1198
+ { source: 'pangram3', length: 30 },
1199
+ ];
1200
+ const testIds = ['doc1', 'doc2', 'doc3'];
1201
+
1202
+ await vectorDB.upsert({
1203
+ indexName: testIndexName3,
1204
+ vectors: testVectors,
1205
+ documents: testDocuments,
1206
+ metadata: testMetadata,
1207
+ ids: testIds,
1208
+ });
1209
+
1210
+ // Wait for indexing
1211
+ await new Promise(resolve => setTimeout(resolve, 2000));
1212
+ });
1213
+
1214
+ afterAll(async () => {
1215
+ // Cleanup after tests
1216
+ try {
1217
+ await vectorDB.deleteIndex(testIndexName3);
1218
+ } catch {
1219
+ // Ignore cleanup errors
1220
+ }
1221
+ });
1222
+
1223
+ describe('Basic Document Operations', () => {
1224
+ it('should store and retrieve documents', async () => {
1225
+ const results = await vectorDB.query({ indexName: testIndexName3, queryVector: [1.0, 0.0, 0.0], topK: 3 });
1226
+ expect(results).toHaveLength(3);
1227
+ // Verify documents are returned
1228
+ expect(results[0].document).toBe(testDocuments[0]);
1229
+ });
1230
+
1231
+ it('should filter documents using $contains', async () => {
1232
+ const results = await vectorDB.query({
1233
+ indexName: testIndexName3,
1234
+ queryVector: [1.0, 0.0, 0.0],
1235
+ topK: 3,
1236
+ documentFilter: { $contains: 'quick' },
1237
+ });
1238
+ expect(results).toHaveLength(2);
1239
+ });
1240
+
1241
+ it('should filter with $not_contains', async () => {
1242
+ const results = await vectorDB.query({
1243
+ indexName: testIndexName3,
1244
+ queryVector: [1.0, 0.0, 0.0],
1245
+ topK: 3,
1246
+ documentFilter: { $not_contains: 'fox' },
1247
+ });
1248
+ expect(results.every(r => !r.document?.includes('fox'))).toBe(true);
1249
+ });
1250
+
1251
+ it('should combine metadata and document filters', async () => {
1252
+ const results = await vectorDB.query({
1253
+ indexName: testIndexName3,
1254
+ queryVector: [1.0, 0.0, 0.0],
1255
+ topK: 3,
1256
+ filter: { source: 'pangram1' },
1257
+ documentFilter: { $contains: 'fox' },
1258
+ });
1259
+ expect(results).toHaveLength(1);
1260
+ expect(results[0].metadata?.source).toBe('pangram1');
1261
+ expect(results[0].document).toContain('fox');
1262
+ });
1263
+ });
1264
+
1265
+ describe('Complex Document Filtering', () => {
1266
+ it('should handle $and conditions', async () => {
1267
+ const results = await vectorDB.query({
1268
+ indexName: testIndexName3,
1269
+ queryVector: [1.0, 0.0, 0.0],
1270
+ topK: 3,
1271
+ documentFilter: { $and: [{ $contains: 'quick' }, { $not_contains: 'fox' }] },
1272
+ });
1273
+ expect(results).toHaveLength(1);
1274
+ expect(results[0].document).toContain('quick');
1275
+ expect(results[0].document).not.toContain('fox');
1276
+ });
1277
+
1278
+ it('should handle $or conditions', async () => {
1279
+ const results = await vectorDB.query({
1280
+ indexName: testIndexName3,
1281
+ queryVector: [1.0, 0.0, 0.0],
1282
+ topK: 3,
1283
+ documentFilter: { $or: [{ $contains: 'fox' }, { $contains: 'zebras' }] },
1284
+ });
1285
+ expect(results).toHaveLength(2);
1286
+ expect(results[0].document).toContain('fox');
1287
+ expect(results[1].document).toContain('zebras');
1288
+ });
1289
+ });
1290
+
1291
+ describe('Edge Cases and Validation', () => {
1292
+ it('allows empty string in $contains', async () => {
1293
+ const results = await vectorDB.query({
1294
+ indexName: testIndexName3,
1295
+ queryVector: [1.0, 0.0, 0.0],
1296
+ topK: 3,
1297
+ documentFilter: { $contains: '' },
1298
+ });
1299
+ expect(results).toHaveLength(3);
1300
+ });
1301
+
1302
+ it('should be case sensitive', async () => {
1303
+ // First verify lowercase works
1304
+ const lowerResults = await vectorDB.query({
1305
+ indexName: testIndexName3,
1306
+ queryVector: [1.0, 0.0, 0.0],
1307
+ topK: 3,
1308
+ documentFilter: { $contains: 'quick' },
1309
+ });
1310
+ expect(lowerResults.length).toBe(2);
1311
+
1312
+ // Then verify uppercase doesn't match
1313
+ const upperResults = await vectorDB.query({
1314
+ indexName: testIndexName3,
1315
+ queryVector: [1.0, 0.0, 0.0],
1316
+ topK: 3,
1317
+ documentFilter: { $contains: 'QUICK' },
1318
+ });
1319
+ expect(upperResults.length).toBe(0);
1320
+
1321
+ const upperResults2 = await vectorDB.query({
1322
+ indexName: testIndexName3,
1323
+ queryVector: [1.0, 0.0, 0.0],
1324
+ topK: 3,
1325
+ documentFilter: { $contains: 'JUMP' },
1326
+ });
1327
+ expect(upperResults2.length).toBe(1);
1328
+ });
1329
+
1330
+ it('should handle exact string matches', async () => {
1331
+ const results = await vectorDB.query({
1332
+ indexName: testIndexName3,
1333
+ queryVector: [1.0, 0.0, 0.0],
1334
+ topK: 3,
1335
+ documentFilter: { $contains: 'quick brown' }, // Test multi-word match
884
1336
  });
1337
+ expect(results.length).toBe(1);
1338
+ expect(results[0].document).toContain('quick brown');
1339
+ });
1340
+
1341
+ it('should handle deeply nested logical operators', async () => {
1342
+ const results = await vectorDB.query({
1343
+ indexName: testIndexName3,
1344
+ queryVector: [1.0, 0.0, 0.0],
1345
+ topK: 3,
1346
+ documentFilter: {
1347
+ $or: [
1348
+ {
1349
+ $and: [{ $contains: 'quick' }, { $not_contains: 'fox' }],
1350
+ },
1351
+ {
1352
+ $and: [{ $contains: 'box' }, { $not_contains: 'quick' }],
1353
+ },
1354
+ ],
1355
+ },
1356
+ });
1357
+ expect(results.length).toBeGreaterThan(0);
1358
+ results.forEach(result => {
1359
+ if (result.document?.includes('quick')) {
1360
+ expect(result.document).not.toContain('fox');
1361
+ } else if (result.document?.includes('box')) {
1362
+ expect(result.document).not.toContain('quick');
1363
+ }
1364
+ });
1365
+ });
1366
+ it('should handle undefined document filter', async () => {
1367
+ const results1 = await vectorDB.query({
1368
+ indexName: testIndexName3,
1369
+ queryVector: [1, 0, 0],
1370
+ documentFilter: undefined,
1371
+ });
1372
+ const results2 = await vectorDB.query({
1373
+ indexName: testIndexName3,
1374
+ queryVector: [1, 0, 0],
1375
+ });
1376
+ expect(results1).toEqual(results2);
1377
+ expect(results1.length).toBeGreaterThan(0);
1378
+ });
1379
+
1380
+ it('should handle empty object document filter', async () => {
1381
+ await expect(
1382
+ vectorDB.query({
1383
+ indexName: testIndexName3,
1384
+ queryVector: [1, 0, 0],
1385
+ documentFilter: {},
1386
+ }),
1387
+ ).rejects.toThrow();
1388
+ });
1389
+
1390
+ it('should handle null filter', async () => {
1391
+ const results = await vectorDB.query({
1392
+ indexName: testIndexName3,
1393
+ queryVector: [1, 0, 0],
1394
+ documentFilter: null,
1395
+ });
1396
+ const results2 = await vectorDB.query({
1397
+ indexName: testIndexName3,
1398
+ queryVector: [1, 0, 0],
1399
+ });
1400
+ expect(results).toEqual(results2);
885
1401
  expect(results.length).toBeGreaterThan(0);
886
1402
  });
887
1403
  });
888
1404
  });
1405
+ describe('Deprecation Warnings', () => {
1406
+ const indexName = 'testdeprecationwarnings';
1407
+
1408
+ const indexName2 = 'testdeprecationwarnings2';
1409
+
1410
+ let warnSpy;
1411
+
1412
+ beforeAll(async () => {
1413
+ await vectorDB.createIndex({ indexName: indexName, dimension: 3 });
1414
+ });
1415
+
1416
+ afterAll(async () => {
1417
+ try {
1418
+ await vectorDB.deleteIndex(indexName);
1419
+ } catch {
1420
+ // Ignore errors if index doesn't exist
1421
+ }
1422
+ try {
1423
+ await vectorDB.deleteIndex(indexName2);
1424
+ } catch {
1425
+ // Ignore errors if index doesn't exist
1426
+ }
1427
+ });
1428
+
1429
+ beforeEach(async () => {
1430
+ warnSpy = vi.spyOn(vectorDB['logger'], 'warn');
1431
+ });
1432
+
1433
+ afterEach(async () => {
1434
+ warnSpy.mockRestore();
1435
+ try {
1436
+ await vectorDB.deleteIndex(indexName2);
1437
+ } catch {
1438
+ // Ignore errors if index doesn't exist
1439
+ }
1440
+ });
1441
+
1442
+ it('should show deprecation warning when using individual args for createIndex', async () => {
1443
+ await vectorDB.createIndex(indexName2, 3, 'cosine');
1444
+
1445
+ expect(warnSpy).toHaveBeenCalledWith(
1446
+ expect.stringContaining('Deprecation Warning: Passing individual arguments to createIndex() is deprecated'),
1447
+ );
1448
+ });
1449
+
1450
+ it('should show deprecation warning when using individual args for upsert', async () => {
1451
+ await vectorDB.upsert(indexName, [[1, 2, 3]], [{ test: 'data' }]);
1452
+
1453
+ expect(warnSpy).toHaveBeenCalledWith(
1454
+ expect.stringContaining('Deprecation Warning: Passing individual arguments to upsert() is deprecated'),
1455
+ );
1456
+ });
1457
+
1458
+ it('should show deprecation warning when using individual args for query', async () => {
1459
+ await vectorDB.query(indexName, [1, 2, 3], 5);
1460
+
1461
+ expect(warnSpy).toHaveBeenCalledWith(
1462
+ expect.stringContaining('Deprecation Warning: Passing individual arguments to query() is deprecated'),
1463
+ );
1464
+ });
1465
+
1466
+ it('should not show deprecation warning when using object param for query', async () => {
1467
+ await vectorDB.query({
1468
+ indexName,
1469
+ queryVector: [1, 2, 3],
1470
+ topK: 5,
1471
+ });
1472
+
1473
+ expect(warnSpy).not.toHaveBeenCalled();
1474
+ });
1475
+
1476
+ it('should not show deprecation warning when using object param for createIndex', async () => {
1477
+ await vectorDB.createIndex({
1478
+ indexName: indexName2,
1479
+ dimension: 3,
1480
+ metric: 'cosine',
1481
+ });
1482
+
1483
+ expect(warnSpy).not.toHaveBeenCalled();
1484
+ });
1485
+
1486
+ it('should not show deprecation warning when using object param for upsert', async () => {
1487
+ await vectorDB.upsert({
1488
+ indexName,
1489
+ vectors: [[1, 2, 3]],
1490
+ metadata: [{ test: 'data' }],
1491
+ });
1492
+
1493
+ expect(warnSpy).not.toHaveBeenCalled();
1494
+ });
1495
+
1496
+ it('should maintain backward compatibility with individual args', async () => {
1497
+ // Query
1498
+ const queryResults = await vectorDB.query(indexName, [1, 2, 3], 5);
1499
+ expect(Array.isArray(queryResults)).toBe(true);
1500
+
1501
+ // CreateIndex
1502
+ await expect(vectorDB.createIndex(indexName2, 3, 'cosine')).resolves.not.toThrow();
1503
+
1504
+ // Upsert
1505
+ const upsertResults = await vectorDB.upsert({
1506
+ indexName,
1507
+ vectors: [[1, 2, 3]],
1508
+ metadata: [{ test: 'data' }],
1509
+ });
1510
+ expect(Array.isArray(upsertResults)).toBe(true);
1511
+ expect(upsertResults).toHaveLength(1);
1512
+ });
1513
+ });
1514
+
1515
+ describe('Performance and Concurrency', () => {
1516
+ const perfTestIndex = 'perf-test-index';
1517
+
1518
+ beforeEach(async () => {
1519
+ try {
1520
+ await vectorDB.deleteIndex(perfTestIndex);
1521
+ } catch {
1522
+ // Ignore errors if index doesn't exist
1523
+ }
1524
+ await vectorDB.createIndex({ indexName: perfTestIndex, dimension });
1525
+ }, 10000);
1526
+
1527
+ afterEach(async () => {
1528
+ try {
1529
+ await vectorDB.deleteIndex(perfTestIndex);
1530
+ } catch {
1531
+ // Ignore cleanup errors
1532
+ }
1533
+ }, 10000);
1534
+
1535
+ it('handles concurrent operations correctly', async () => {
1536
+ const promises = Array(10)
1537
+ .fill(0)
1538
+ .map((_, i) =>
1539
+ vectorDB.upsert({
1540
+ indexName: perfTestIndex,
1541
+ vectors: [[1, 0, 0]],
1542
+ metadata: [{ test: 'concurrent', id: i }],
1543
+ ids: [`concurrent-${i}`],
1544
+ }),
1545
+ );
1546
+ await Promise.all(promises);
1547
+
1548
+ const results = await vectorDB.query({
1549
+ indexName: perfTestIndex,
1550
+ queryVector: [1, 0, 0],
1551
+ filter: { test: 'concurrent' },
1552
+ });
1553
+ expect(results).toHaveLength(10);
1554
+ }, 15000);
1555
+
1556
+ it('handles large batch operations', async () => {
1557
+ const batchSize = 100; // Using 100 instead of 1000 to keep tests fast
1558
+ const vectors = Array(batchSize)
1559
+ .fill(0)
1560
+ .map(() => [1, 0, 0]);
1561
+ const metadata = vectors.map((_, i) => ({ index: i, test: 'batch' }));
1562
+ const ids = vectors.map((_, i) => `batch-${i}`);
1563
+
1564
+ await vectorDB.upsert({
1565
+ indexName: perfTestIndex,
1566
+ vectors,
1567
+ metadata,
1568
+ ids,
1569
+ });
1570
+
1571
+ // Verify all vectors were inserted
1572
+ const stats = await vectorDB.describeIndex(perfTestIndex);
1573
+ expect(stats.count).toBe(batchSize);
1574
+
1575
+ const results = await vectorDB.query({
1576
+ indexName: perfTestIndex,
1577
+ queryVector: [1, 0, 0],
1578
+ filter: { test: 'batch' },
1579
+ topK: batchSize,
1580
+ });
1581
+ expect(results).toHaveLength(batchSize);
1582
+
1583
+ // Test querying with pagination
1584
+ const pageSize = 20;
1585
+ const pages: QueryResult[][] = [];
1586
+ for (let i = 0; i < batchSize; i += pageSize) {
1587
+ const page = await vectorDB.query({
1588
+ indexName: perfTestIndex,
1589
+ queryVector: [1, 0, 0],
1590
+ filter: { test: 'batch' },
1591
+ topK: pageSize,
1592
+ });
1593
+ pages.push(page);
1594
+ expect(page).toHaveLength(Math.min(pageSize, batchSize - i));
1595
+ }
1596
+ expect(pages).toHaveLength(Math.ceil(batchSize / pageSize));
1597
+ }, 30000);
1598
+ });
889
1599
  });