@mastra/chroma 0.1.6-alpha.1 → 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.
- package/.turbo/turbo-build.log +11 -6
- package/CHANGELOG.md +24 -0
- package/README.md +16 -17
- package/dist/_tsup-dts-rollup.d.cts +56 -0
- package/dist/_tsup-dts-rollup.d.ts +20 -15
- package/dist/index.cjs +214 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +12 -7
- package/package.json +7 -3
- package/src/vector/filter.ts +5 -5
- package/src/vector/index.test.ts +563 -224
- package/src/vector/index.ts +33 -29
package/src/vector/index.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { QueryResult, IndexStats } from '@mastra/core/vector';
|
|
2
|
-
import { describe, expect, beforeEach, afterEach, it, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import { describe, expect, beforeEach, afterEach, it, beforeAll, afterAll, vi } from 'vitest';
|
|
3
3
|
|
|
4
4
|
import { ChromaVector } from './';
|
|
5
5
|
|
|
@@ -20,7 +20,7 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
20
20
|
} catch {
|
|
21
21
|
// Ignore errors if index doesn't exist
|
|
22
22
|
}
|
|
23
|
-
await vectorDB.createIndex(testIndexName, dimension);
|
|
23
|
+
await vectorDB.createIndex({ indexName: testIndexName, dimension });
|
|
24
24
|
}, 5000);
|
|
25
25
|
|
|
26
26
|
afterEach(async () => {
|
|
@@ -56,7 +56,7 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
56
56
|
|
|
57
57
|
for (const metric of metricsToTest) {
|
|
58
58
|
const testIndex = `test-index-${metric}`;
|
|
59
|
-
await vectorDB.createIndex(testIndex, dimension, metric);
|
|
59
|
+
await vectorDB.createIndex({ indexName: testIndex, dimension, metric });
|
|
60
60
|
|
|
61
61
|
const stats = await vectorDB.describeIndex(testIndex);
|
|
62
62
|
expect(stats.metric).toBe(metric);
|
|
@@ -76,7 +76,7 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
76
76
|
const testIds = ['vec1', 'vec2', 'vec3'];
|
|
77
77
|
|
|
78
78
|
it('should upsert vectors with generated ids', async () => {
|
|
79
|
-
const ids = await vectorDB.upsert(testIndexName, testVectors);
|
|
79
|
+
const ids = await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors });
|
|
80
80
|
expect(ids).toHaveLength(testVectors.length);
|
|
81
81
|
ids.forEach(id => expect(typeof id).toBe('string'));
|
|
82
82
|
|
|
@@ -85,14 +85,14 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
85
85
|
});
|
|
86
86
|
|
|
87
87
|
it('should upsert vectors with provided ids and metadata', async () => {
|
|
88
|
-
await vectorDB.upsert(testIndexName, testVectors, testMetadata, testIds);
|
|
88
|
+
await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors, metadata: testMetadata, ids: testIds });
|
|
89
89
|
|
|
90
90
|
const stats = await vectorDB.describeIndex(testIndexName);
|
|
91
91
|
expect(stats.count).toBe(testVectors.length);
|
|
92
92
|
|
|
93
93
|
// Query each vector to verify metadata
|
|
94
94
|
for (let i = 0; i < testVectors.length; i++) {
|
|
95
|
-
const results = await vectorDB.query(testIndexName, testVectors?.[i]!, 1);
|
|
95
|
+
const results = await vectorDB.query({ indexName: testIndexName, queryVector: testVectors?.[i]!, topK: 1 });
|
|
96
96
|
expect(results?.[0]?.id).toBe(testIds[i]);
|
|
97
97
|
expect(results?.[0]?.metadata).toEqual(testMetadata[i]);
|
|
98
98
|
}
|
|
@@ -100,15 +100,20 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
100
100
|
|
|
101
101
|
it('should update existing vectors', async () => {
|
|
102
102
|
// Initial upsert
|
|
103
|
-
await vectorDB.upsert(testIndexName, testVectors, testMetadata, testIds);
|
|
103
|
+
await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors, metadata: testMetadata, ids: testIds });
|
|
104
104
|
|
|
105
105
|
// Update first vector
|
|
106
106
|
const updatedVector = [[0.5, 0.5, 0.0]];
|
|
107
107
|
const updatedMetadata = [{ label: 'updated-x-axis' }];
|
|
108
|
-
await vectorDB.upsert(
|
|
108
|
+
await vectorDB.upsert({
|
|
109
|
+
indexName: testIndexName,
|
|
110
|
+
vectors: updatedVector,
|
|
111
|
+
metadata: updatedMetadata,
|
|
112
|
+
ids: [testIds?.[0]!],
|
|
113
|
+
});
|
|
109
114
|
|
|
110
115
|
// Verify update
|
|
111
|
-
const results = await vectorDB.query(testIndexName, updatedVector?.[0]!, 1);
|
|
116
|
+
const results = await vectorDB.query({ indexName: testIndexName, queryVector: updatedVector?.[0]!, topK: 1 });
|
|
112
117
|
expect(results?.[0]?.id).toBe(testIds[0]);
|
|
113
118
|
expect(results?.[0]?.metadata).toEqual(updatedMetadata[0]);
|
|
114
119
|
});
|
|
@@ -124,7 +129,7 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
124
129
|
const testIds = ['vec1', 'vec2', 'vec3'];
|
|
125
130
|
|
|
126
131
|
beforeEach(async () => {
|
|
127
|
-
await vectorDB.upsert(testIndexName, testVectors, testMetadata, testIds);
|
|
132
|
+
await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors, metadata: testMetadata, ids: testIds });
|
|
128
133
|
});
|
|
129
134
|
|
|
130
135
|
describe('Basic Queries', () => {
|
|
@@ -132,7 +137,7 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
132
137
|
const queryVector = [1.0, 0.1, 0.1];
|
|
133
138
|
const topK = 2;
|
|
134
139
|
|
|
135
|
-
const results: QueryResult[] = await vectorDB.query(testIndexName, queryVector, topK);
|
|
140
|
+
const results: QueryResult[] = await vectorDB.query({ indexName: testIndexName, queryVector, topK });
|
|
136
141
|
|
|
137
142
|
expect(results).toHaveLength(topK);
|
|
138
143
|
expect(results?.[0]?.id).toBe(testIds[0]); // Should match x-axis vector most closely
|
|
@@ -144,7 +149,7 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
144
149
|
const queryVector = [1.0, 1.0, 1.0];
|
|
145
150
|
const filter = { label: 'x-axis' };
|
|
146
151
|
|
|
147
|
-
const results = await vectorDB.query(testIndexName, queryVector, 3, filter);
|
|
152
|
+
const results = await vectorDB.query({ indexName: testIndexName, queryVector, topK: 3, filter });
|
|
148
153
|
|
|
149
154
|
expect(results).toHaveLength(1);
|
|
150
155
|
expect(results?.[0]?.metadata?.label).toBe('x-axis');
|
|
@@ -156,7 +161,12 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
156
161
|
const queryVector = [1.0, 0.1, 0.1];
|
|
157
162
|
const topK = 1;
|
|
158
163
|
|
|
159
|
-
const results = await vectorDB.query(
|
|
164
|
+
const results = await vectorDB.query({
|
|
165
|
+
indexName: testIndexName,
|
|
166
|
+
queryVector,
|
|
167
|
+
topK,
|
|
168
|
+
includeVector: true,
|
|
169
|
+
});
|
|
160
170
|
|
|
161
171
|
expect(results).toHaveLength(topK);
|
|
162
172
|
expect(results?.[0]?.vector).toEqual(testVectors[0]);
|
|
@@ -166,73 +176,95 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
166
176
|
|
|
167
177
|
describe('Error Handling', () => {
|
|
168
178
|
it('should handle non-existent index queries', async () => {
|
|
169
|
-
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();
|
|
170
180
|
});
|
|
171
181
|
|
|
172
182
|
it('should handle invalid dimension vectors', async () => {
|
|
173
183
|
const invalidVector = [1, 2, 3, 4]; // 4D vector for 3D index
|
|
174
|
-
await expect(vectorDB.upsert(testIndexName, [invalidVector])).rejects.toThrow();
|
|
184
|
+
await expect(vectorDB.upsert({ indexName: testIndexName, vectors: [invalidVector] })).rejects.toThrow();
|
|
175
185
|
});
|
|
176
186
|
|
|
177
187
|
it('should handle mismatched metadata and vectors length', async () => {
|
|
178
188
|
const vectors = [[1, 2, 3]];
|
|
179
189
|
const metadata = [{}, {}]; // More metadata than vectors
|
|
180
|
-
await expect(vectorDB.upsert(testIndexName, vectors, metadata)).rejects.toThrow();
|
|
190
|
+
await expect(vectorDB.upsert({ indexName: testIndexName, vectors, metadata })).rejects.toThrow();
|
|
181
191
|
});
|
|
182
192
|
});
|
|
183
193
|
|
|
184
194
|
describe('Filter Validation in Queries', () => {
|
|
185
195
|
it('rejects queries with null values', async () => {
|
|
186
196
|
await expect(
|
|
187
|
-
vectorDB.query(
|
|
188
|
-
|
|
197
|
+
vectorDB.query({
|
|
198
|
+
indexName: testIndexName,
|
|
199
|
+
queryVector: [1, 0, 0],
|
|
200
|
+
filter: {
|
|
201
|
+
field: null,
|
|
202
|
+
},
|
|
189
203
|
}),
|
|
190
204
|
).rejects.toThrow();
|
|
191
205
|
|
|
192
206
|
await expect(
|
|
193
|
-
vectorDB.query(
|
|
194
|
-
|
|
207
|
+
vectorDB.query({
|
|
208
|
+
indexName: testIndexName,
|
|
209
|
+
queryVector: [1, 0, 0],
|
|
210
|
+
filter: {
|
|
211
|
+
other: { $eq: null },
|
|
212
|
+
},
|
|
195
213
|
}),
|
|
196
214
|
).rejects.toThrow();
|
|
197
215
|
});
|
|
198
216
|
|
|
199
217
|
it('validates array operator values', async () => {
|
|
200
218
|
await expect(
|
|
201
|
-
vectorDB.query(
|
|
202
|
-
|
|
219
|
+
vectorDB.query({
|
|
220
|
+
indexName: testIndexName,
|
|
221
|
+
queryVector: [1, 0, 0],
|
|
222
|
+
filter: {
|
|
223
|
+
tags: { $in: null },
|
|
224
|
+
},
|
|
203
225
|
}),
|
|
204
226
|
).rejects.toThrow();
|
|
205
227
|
});
|
|
206
228
|
|
|
207
229
|
it('validates numeric values for comparison operators', async () => {
|
|
208
230
|
await expect(
|
|
209
|
-
vectorDB.query(
|
|
210
|
-
|
|
231
|
+
vectorDB.query({
|
|
232
|
+
indexName: testIndexName,
|
|
233
|
+
queryVector: [1, 0, 0],
|
|
234
|
+
filter: {
|
|
235
|
+
price: { $gt: 'not-a-number' },
|
|
236
|
+
},
|
|
211
237
|
}),
|
|
212
238
|
).rejects.toThrow();
|
|
213
239
|
});
|
|
214
240
|
|
|
215
241
|
it('validates value types', async () => {
|
|
216
242
|
await expect(
|
|
217
|
-
vectorDB.query(
|
|
218
|
-
|
|
243
|
+
vectorDB.query({
|
|
244
|
+
indexName: testIndexName,
|
|
245
|
+
queryVector: [1, 0, 0],
|
|
246
|
+
filter: { date: { $gt: 'not-a-date' } },
|
|
219
247
|
}),
|
|
220
248
|
).rejects.toThrow();
|
|
221
249
|
|
|
222
250
|
await expect(
|
|
223
|
-
vectorDB.query(
|
|
224
|
-
|
|
251
|
+
vectorDB.query({
|
|
252
|
+
indexName: testIndexName,
|
|
253
|
+
queryVector: [1, 0, 0],
|
|
254
|
+
filter: { number: { $lt: 'not-a-number' } },
|
|
225
255
|
}),
|
|
226
256
|
).rejects.toThrow();
|
|
227
257
|
});
|
|
228
258
|
|
|
229
259
|
it('validates array operators', async () => {
|
|
230
|
-
const invalidValues = [123, 'string', true, { key: 'value' }, null
|
|
260
|
+
const invalidValues = [123, 'string', true, { key: 'value' }, null];
|
|
231
261
|
for (const op of ['$in', '$nin']) {
|
|
232
262
|
for (const val of invalidValues) {
|
|
233
263
|
await expect(
|
|
234
|
-
vectorDB.query(
|
|
235
|
-
|
|
264
|
+
vectorDB.query({
|
|
265
|
+
indexName: testIndexName,
|
|
266
|
+
queryVector: [1, 0, 0],
|
|
267
|
+
filter: { field: { [op]: val } },
|
|
236
268
|
}),
|
|
237
269
|
).rejects.toThrow();
|
|
238
270
|
}
|
|
@@ -245,8 +277,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
245
277
|
for (const op of ['$in', '$nin']) {
|
|
246
278
|
for (const val of invalidValues) {
|
|
247
279
|
await expect(
|
|
248
|
-
vectorDB.query(
|
|
249
|
-
|
|
280
|
+
vectorDB.query({
|
|
281
|
+
indexName: testIndexName,
|
|
282
|
+
queryVector: [1, 0, 0],
|
|
283
|
+
filter: { field: { [op]: val } },
|
|
250
284
|
}),
|
|
251
285
|
).rejects.toThrow();
|
|
252
286
|
}
|
|
@@ -257,8 +291,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
257
291
|
// Basic equality can accept any non-undefined value
|
|
258
292
|
for (const op of ['$eq', '$ne']) {
|
|
259
293
|
await expect(
|
|
260
|
-
vectorDB.query(
|
|
261
|
-
|
|
294
|
+
vectorDB.query({
|
|
295
|
+
indexName: testIndexName,
|
|
296
|
+
queryVector: [1, 0, 0],
|
|
297
|
+
filter: { field: { [op]: undefined } },
|
|
262
298
|
}),
|
|
263
299
|
).rejects.toThrow();
|
|
264
300
|
}
|
|
@@ -269,8 +305,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
269
305
|
for (const op of numOps) {
|
|
270
306
|
for (const val of invalidNumericValues) {
|
|
271
307
|
await expect(
|
|
272
|
-
vectorDB.query(
|
|
273
|
-
|
|
308
|
+
vectorDB.query({
|
|
309
|
+
indexName: testIndexName,
|
|
310
|
+
queryVector: [1, 0, 0],
|
|
311
|
+
filter: { field: { [op]: val } },
|
|
274
312
|
}),
|
|
275
313
|
).rejects.toThrow();
|
|
276
314
|
}
|
|
@@ -279,29 +317,45 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
279
317
|
|
|
280
318
|
it('validates multiple invalid values', async () => {
|
|
281
319
|
await expect(
|
|
282
|
-
vectorDB.query(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
+
},
|
|
286
328
|
}),
|
|
287
329
|
).rejects.toThrow();
|
|
288
330
|
});
|
|
289
331
|
|
|
290
332
|
it('handles empty object filters', async () => {
|
|
291
333
|
// Test empty object at top level
|
|
292
|
-
await expect(
|
|
334
|
+
await expect(
|
|
335
|
+
vectorDB.query({
|
|
336
|
+
indexName: testIndexName,
|
|
337
|
+
queryVector: [1, 0, 0],
|
|
338
|
+
filter: { field: { $eq: {} } },
|
|
339
|
+
}),
|
|
340
|
+
).rejects.toThrow();
|
|
293
341
|
});
|
|
294
342
|
|
|
295
343
|
it('handles empty/undefined filters by returning all results', async () => {
|
|
296
344
|
const noFilterCases = [{ field: {} }, { field: undefined }, { field: { $in: undefined } }];
|
|
297
345
|
|
|
298
346
|
for (const filter of noFilterCases) {
|
|
299
|
-
await expect(vectorDB.query(testIndexName, [1, 0, 0],
|
|
347
|
+
await expect(vectorDB.query({ indexName: testIndexName, queryVector: [1, 0, 0], filter })).rejects.toThrow();
|
|
300
348
|
}
|
|
301
349
|
});
|
|
302
350
|
it('handles empty object filters', async () => {
|
|
303
351
|
// Test empty object at top level
|
|
304
|
-
await expect(
|
|
352
|
+
await expect(
|
|
353
|
+
vectorDB.query({
|
|
354
|
+
indexName: testIndexName,
|
|
355
|
+
queryVector: [1, 0, 0],
|
|
356
|
+
filter: {},
|
|
357
|
+
}),
|
|
358
|
+
).rejects.toThrow();
|
|
305
359
|
});
|
|
306
360
|
});
|
|
307
361
|
|
|
@@ -313,7 +367,7 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
313
367
|
} catch {
|
|
314
368
|
// Ignore errors if index doesn't exist
|
|
315
369
|
}
|
|
316
|
-
await vectorDB.createIndex(testIndexName2, dimension);
|
|
370
|
+
await vectorDB.createIndex({ indexName: testIndexName2, dimension });
|
|
317
371
|
|
|
318
372
|
const vectors = [
|
|
319
373
|
[1, 0, 0], // Electronics
|
|
@@ -349,7 +403,7 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
349
403
|
},
|
|
350
404
|
];
|
|
351
405
|
|
|
352
|
-
await vectorDB.upsert(testIndexName2, vectors, metadata);
|
|
406
|
+
await vectorDB.upsert({ indexName: testIndexName2, vectors, metadata });
|
|
353
407
|
// Wait for indexing
|
|
354
408
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
355
409
|
});
|
|
@@ -365,8 +419,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
365
419
|
|
|
366
420
|
describe('Basic Comparison Operators', () => {
|
|
367
421
|
it('filters with $eq operator', async () => {
|
|
368
|
-
const results = await vectorDB.query(
|
|
369
|
-
|
|
422
|
+
const results = await vectorDB.query({
|
|
423
|
+
indexName: testIndexName2,
|
|
424
|
+
queryVector: [1, 0, 0],
|
|
425
|
+
filter: { category: { $eq: 'electronics' } },
|
|
370
426
|
});
|
|
371
427
|
expect(results.length).toBe(2);
|
|
372
428
|
results.forEach(result => {
|
|
@@ -375,8 +431,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
375
431
|
});
|
|
376
432
|
|
|
377
433
|
it('filters with implicit $eq', async () => {
|
|
378
|
-
const results = await vectorDB.query(
|
|
379
|
-
|
|
434
|
+
const results = await vectorDB.query({
|
|
435
|
+
indexName: testIndexName2,
|
|
436
|
+
queryVector: [1, 0, 0],
|
|
437
|
+
filter: { category: 'electronics' }, // implicit $eq
|
|
380
438
|
});
|
|
381
439
|
expect(results.length).toBe(2);
|
|
382
440
|
results.forEach(result => {
|
|
@@ -384,8 +442,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
384
442
|
});
|
|
385
443
|
});
|
|
386
444
|
it('filters with $gt operator', async () => {
|
|
387
|
-
const results = await vectorDB.query(
|
|
388
|
-
|
|
445
|
+
const results = await vectorDB.query({
|
|
446
|
+
indexName: testIndexName2,
|
|
447
|
+
queryVector: [1, 0, 0],
|
|
448
|
+
filter: { price: { $gt: 500 } },
|
|
389
449
|
});
|
|
390
450
|
expect(results.length).toBe(1);
|
|
391
451
|
results.forEach(result => {
|
|
@@ -394,8 +454,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
394
454
|
});
|
|
395
455
|
|
|
396
456
|
it('filters with $gte operator', async () => {
|
|
397
|
-
const results = await vectorDB.query(
|
|
398
|
-
|
|
457
|
+
const results = await vectorDB.query({
|
|
458
|
+
indexName: testIndexName2,
|
|
459
|
+
queryVector: [1, 0, 0],
|
|
460
|
+
filter: { price: { $gte: 500 } },
|
|
399
461
|
});
|
|
400
462
|
expect(results.length).toBe(2);
|
|
401
463
|
results.forEach(result => {
|
|
@@ -404,8 +466,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
404
466
|
});
|
|
405
467
|
|
|
406
468
|
it('filters with $lt operator', async () => {
|
|
407
|
-
const results = await vectorDB.query(
|
|
408
|
-
|
|
469
|
+
const results = await vectorDB.query({
|
|
470
|
+
indexName: testIndexName2,
|
|
471
|
+
queryVector: [1, 0, 0],
|
|
472
|
+
filter: { price: { $lt: 100 } },
|
|
409
473
|
});
|
|
410
474
|
expect(results.length).toBe(2);
|
|
411
475
|
results.forEach(result => {
|
|
@@ -414,8 +478,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
414
478
|
});
|
|
415
479
|
|
|
416
480
|
it('filters with $lte operator', async () => {
|
|
417
|
-
const results = await vectorDB.query(
|
|
418
|
-
|
|
481
|
+
const results = await vectorDB.query({
|
|
482
|
+
indexName: testIndexName2,
|
|
483
|
+
queryVector: [1, 0, 0],
|
|
484
|
+
filter: { price: { $lte: 500 } },
|
|
419
485
|
});
|
|
420
486
|
expect(results.length).toBeGreaterThan(0);
|
|
421
487
|
results.forEach(result => {
|
|
@@ -424,8 +490,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
424
490
|
});
|
|
425
491
|
|
|
426
492
|
it('filters with $gte, $lt, $lte operators', async () => {
|
|
427
|
-
const results = await vectorDB.query(
|
|
428
|
-
|
|
493
|
+
const results = await vectorDB.query({
|
|
494
|
+
indexName: testIndexName2,
|
|
495
|
+
queryVector: [1, 0, 0],
|
|
496
|
+
filter: { price: { $gte: 25, $lte: 500 } },
|
|
429
497
|
});
|
|
430
498
|
expect(results.length).toBe(2);
|
|
431
499
|
results.forEach(result => {
|
|
@@ -435,8 +503,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
435
503
|
});
|
|
436
504
|
|
|
437
505
|
it('filters with $ne operator', async () => {
|
|
438
|
-
const results = await vectorDB.query(
|
|
439
|
-
|
|
506
|
+
const results = await vectorDB.query({
|
|
507
|
+
indexName: testIndexName2,
|
|
508
|
+
queryVector: [1, 0, 0],
|
|
509
|
+
filter: { category: { $ne: 'electronics' } },
|
|
440
510
|
});
|
|
441
511
|
expect(results.length).toBe(2);
|
|
442
512
|
results.forEach(result => {
|
|
@@ -445,8 +515,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
445
515
|
});
|
|
446
516
|
|
|
447
517
|
it('filters with boolean values', async () => {
|
|
448
|
-
const results = await vectorDB.query(
|
|
449
|
-
|
|
518
|
+
const results = await vectorDB.query({
|
|
519
|
+
indexName: testIndexName2,
|
|
520
|
+
queryVector: [1, 0, 0],
|
|
521
|
+
filter: { inStock: true }, // test both implicit
|
|
450
522
|
});
|
|
451
523
|
expect(results.length).toBe(3);
|
|
452
524
|
results.forEach(result => {
|
|
@@ -455,9 +527,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
455
527
|
});
|
|
456
528
|
|
|
457
529
|
it('filters with multiple fields', async () => {
|
|
458
|
-
const results = await vectorDB.query(
|
|
459
|
-
|
|
460
|
-
|
|
530
|
+
const results = await vectorDB.query({
|
|
531
|
+
indexName: testIndexName2,
|
|
532
|
+
queryVector: [1, 0, 0],
|
|
533
|
+
filter: { category: 'electronics', price: 1000 },
|
|
461
534
|
});
|
|
462
535
|
expect(results.length).toBeGreaterThan(0);
|
|
463
536
|
results.forEach(result => {
|
|
@@ -468,8 +541,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
468
541
|
|
|
469
542
|
describe('Array Operators', () => {
|
|
470
543
|
it('filters with $in operator', async () => {
|
|
471
|
-
const results = await vectorDB.query(
|
|
472
|
-
|
|
544
|
+
const results = await vectorDB.query({
|
|
545
|
+
indexName: testIndexName2,
|
|
546
|
+
queryVector: [1, 0, 0],
|
|
547
|
+
filter: { category: { $in: ['electronics', 'books'] } },
|
|
473
548
|
});
|
|
474
549
|
expect(results.length).toBeGreaterThan(0);
|
|
475
550
|
results.forEach(result => {
|
|
@@ -478,8 +553,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
478
553
|
});
|
|
479
554
|
|
|
480
555
|
it('should filter with $in operator for numbers', async () => {
|
|
481
|
-
const results = await vectorDB.query(
|
|
482
|
-
|
|
556
|
+
const results = await vectorDB.query({
|
|
557
|
+
indexName: testIndexName2,
|
|
558
|
+
queryVector: [1, 0, 0],
|
|
559
|
+
filter: { price: { $in: [50, 75, 1000] } },
|
|
483
560
|
});
|
|
484
561
|
expect(results.length).toBeGreaterThan(0);
|
|
485
562
|
results.forEach(result => {
|
|
@@ -488,8 +565,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
488
565
|
});
|
|
489
566
|
|
|
490
567
|
it('filters with $in operator for booleans', async () => {
|
|
491
|
-
const results = await vectorDB.query(
|
|
492
|
-
|
|
568
|
+
const results = await vectorDB.query({
|
|
569
|
+
indexName: testIndexName2,
|
|
570
|
+
queryVector: [1, 0, 0],
|
|
571
|
+
filter: { inStock: { $in: [true] } },
|
|
493
572
|
});
|
|
494
573
|
expect(results.length).toBeGreaterThan(0);
|
|
495
574
|
results.forEach(result => {
|
|
@@ -500,8 +579,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
500
579
|
|
|
501
580
|
describe('Logical Operators', () => {
|
|
502
581
|
it('filters with $and operator', async () => {
|
|
503
|
-
const results = await vectorDB.query(
|
|
504
|
-
|
|
582
|
+
const results = await vectorDB.query({
|
|
583
|
+
indexName: testIndexName2,
|
|
584
|
+
queryVector: [1, 0, 0],
|
|
585
|
+
filter: { $and: [{ category: 'electronics' }, { price: { $gt: 500 } }] },
|
|
505
586
|
});
|
|
506
587
|
expect(results.length).toBe(1);
|
|
507
588
|
expect(results[0]?.metadata?.category).toBe('electronics');
|
|
@@ -509,8 +590,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
509
590
|
});
|
|
510
591
|
|
|
511
592
|
it('should filter with $and operator', async () => {
|
|
512
|
-
const results = await vectorDB.query(
|
|
513
|
-
|
|
593
|
+
const results = await vectorDB.query({
|
|
594
|
+
indexName: testIndexName2,
|
|
595
|
+
queryVector: [1, 0, 0],
|
|
596
|
+
filter: { $and: [{ category: 'electronics' }, { price: { $gt: 700 } }, { inStock: true }] },
|
|
514
597
|
});
|
|
515
598
|
expect(results.length).toBeGreaterThan(0);
|
|
516
599
|
results.forEach(result => {
|
|
@@ -521,8 +604,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
521
604
|
});
|
|
522
605
|
|
|
523
606
|
it('filters with $or operator', async () => {
|
|
524
|
-
const results = await vectorDB.query(
|
|
525
|
-
|
|
607
|
+
const results = await vectorDB.query({
|
|
608
|
+
indexName: testIndexName2,
|
|
609
|
+
queryVector: [1, 0, 0],
|
|
610
|
+
filter: { $or: [{ price: { $gt: 900 } }, { rating: { $gt: 4.8 } }] },
|
|
526
611
|
});
|
|
527
612
|
expect(results.length).toBe(2);
|
|
528
613
|
results.forEach(result => {
|
|
@@ -531,8 +616,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
531
616
|
});
|
|
532
617
|
|
|
533
618
|
it('should filter with $or operator', async () => {
|
|
534
|
-
const results = await vectorDB.query(
|
|
535
|
-
|
|
619
|
+
const results = await vectorDB.query({
|
|
620
|
+
indexName: testIndexName2,
|
|
621
|
+
queryVector: [1, 0, 0],
|
|
622
|
+
filter: { $or: [{ price: { $gt: 900 } }, { category: { $in: ['electronics', 'books'] } }] },
|
|
536
623
|
});
|
|
537
624
|
expect(results.length).toBeGreaterThan(0);
|
|
538
625
|
results.forEach(result => {
|
|
@@ -543,14 +630,18 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
543
630
|
});
|
|
544
631
|
|
|
545
632
|
it('should handle nested logical operators', async () => {
|
|
546
|
-
const results = await vectorDB.query(
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
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
|
+
},
|
|
554
645
|
});
|
|
555
646
|
expect(results.length).toBeGreaterThan(0);
|
|
556
647
|
results.forEach(result => {
|
|
@@ -561,22 +652,28 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
561
652
|
});
|
|
562
653
|
|
|
563
654
|
it('uses implicit $eq within $or', async () => {
|
|
564
|
-
const results = await vectorDB.query(
|
|
565
|
-
|
|
655
|
+
const results = await vectorDB.query({
|
|
656
|
+
indexName: testIndexName2,
|
|
657
|
+
queryVector: [1, 0, 0],
|
|
658
|
+
filter: { $or: [{ category: 'electronics' }, { price: { $gt: 100 } }] },
|
|
566
659
|
});
|
|
567
660
|
expect(results.length).toBeGreaterThan(0);
|
|
568
661
|
});
|
|
569
662
|
|
|
570
663
|
it('requires multiple conditions in logical operators', async () => {
|
|
571
664
|
await expect(
|
|
572
|
-
vectorDB.query(
|
|
573
|
-
|
|
665
|
+
vectorDB.query({
|
|
666
|
+
indexName: testIndexName2,
|
|
667
|
+
queryVector: [1, 0, 0],
|
|
668
|
+
filter: { $and: [{ category: 'electronics' }] },
|
|
574
669
|
}),
|
|
575
670
|
).rejects.toThrow();
|
|
576
671
|
|
|
577
672
|
await expect(
|
|
578
|
-
vectorDB.query(
|
|
579
|
-
|
|
673
|
+
vectorDB.query({
|
|
674
|
+
indexName: testIndexName2,
|
|
675
|
+
queryVector: [1, 0, 0],
|
|
676
|
+
filter: { $or: [{ price: { $gt: 900 } }] },
|
|
580
677
|
}),
|
|
581
678
|
).rejects.toThrow();
|
|
582
679
|
});
|
|
@@ -584,14 +681,18 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
584
681
|
|
|
585
682
|
describe('Complex Filter Combinations', () => {
|
|
586
683
|
it('combines multiple operators and conditions', async () => {
|
|
587
|
-
const results = await vectorDB.query(
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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
|
+
},
|
|
595
696
|
});
|
|
596
697
|
expect(results.length).toBeGreaterThan(0);
|
|
597
698
|
results.forEach(result => {
|
|
@@ -602,15 +703,19 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
602
703
|
});
|
|
603
704
|
|
|
604
705
|
it('handles complex nested conditions', async () => {
|
|
605
|
-
const results = await vectorDB.query(
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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
|
+
},
|
|
614
719
|
});
|
|
615
720
|
expect(results.length).toBeGreaterThan(0);
|
|
616
721
|
results.forEach(result => {
|
|
@@ -623,8 +728,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
623
728
|
});
|
|
624
729
|
|
|
625
730
|
it('should combine comparison and array operators', async () => {
|
|
626
|
-
const results = await vectorDB.query(
|
|
627
|
-
|
|
731
|
+
const results = await vectorDB.query({
|
|
732
|
+
indexName: testIndexName2,
|
|
733
|
+
queryVector: [1, 0, 0],
|
|
734
|
+
filter: { $and: [{ price: { $gte: 500 } }, { rating: { $gt: 4.5 } }] },
|
|
628
735
|
});
|
|
629
736
|
expect(results.length).toBeGreaterThan(0);
|
|
630
737
|
results.forEach(result => {
|
|
@@ -634,8 +741,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
634
741
|
});
|
|
635
742
|
|
|
636
743
|
it('should handle multiple conditions on same field', async () => {
|
|
637
|
-
const results = await vectorDB.query(
|
|
638
|
-
|
|
744
|
+
const results = await vectorDB.query({
|
|
745
|
+
indexName: testIndexName2,
|
|
746
|
+
queryVector: [1, 0, 0],
|
|
747
|
+
filter: { $and: [{ price: { $gte: 30 } }, { price: { $lte: 800 } }] },
|
|
639
748
|
});
|
|
640
749
|
expect(results.length).toBeGreaterThan(0);
|
|
641
750
|
results.forEach(result => {
|
|
@@ -646,15 +755,19 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
646
755
|
});
|
|
647
756
|
|
|
648
757
|
it('should handle deeply nested logical operators', async () => {
|
|
649
|
-
const results = await vectorDB.query(
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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
|
+
},
|
|
658
771
|
});
|
|
659
772
|
expect(results.length).toBeGreaterThan(0);
|
|
660
773
|
results.forEach(result => {
|
|
@@ -702,13 +815,19 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
702
815
|
},
|
|
703
816
|
];
|
|
704
817
|
|
|
705
|
-
await vectorDB.upsert(
|
|
818
|
+
await vectorDB.upsert({
|
|
819
|
+
indexName: testIndexName2,
|
|
820
|
+
vectors,
|
|
821
|
+
metadata,
|
|
822
|
+
});
|
|
706
823
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
707
824
|
});
|
|
708
825
|
|
|
709
826
|
it('handles special numeric values', async () => {
|
|
710
|
-
const results = await vectorDB.query(
|
|
711
|
-
|
|
827
|
+
const results = await vectorDB.query({
|
|
828
|
+
indexName: testIndexName2,
|
|
829
|
+
queryVector: [1, 0, 0],
|
|
830
|
+
filter: { $or: [{ zero: 0 }, { negativeZero: 0 }] },
|
|
712
831
|
});
|
|
713
832
|
expect(results.length).toBeGreaterThan(0);
|
|
714
833
|
results.forEach(result => {
|
|
@@ -718,15 +837,23 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
718
837
|
});
|
|
719
838
|
|
|
720
839
|
it('handles extreme numeric values', async () => {
|
|
721
|
-
const results = await vectorDB.query(
|
|
722
|
-
|
|
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
|
+
},
|
|
723
846
|
});
|
|
724
847
|
expect(results.length).toBe(1);
|
|
725
848
|
});
|
|
726
849
|
|
|
727
850
|
it('should handle numeric comparisons with decimals', async () => {
|
|
728
|
-
const results = await vectorDB.query(
|
|
729
|
-
|
|
851
|
+
const results = await vectorDB.query({
|
|
852
|
+
indexName: testIndexName2,
|
|
853
|
+
queryVector: [1, 0, 0],
|
|
854
|
+
filter: {
|
|
855
|
+
rating: { $gt: 4.5 },
|
|
856
|
+
},
|
|
730
857
|
});
|
|
731
858
|
expect(results.length).toBeGreaterThan(0);
|
|
732
859
|
results.forEach(result => {
|
|
@@ -735,8 +862,10 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
735
862
|
});
|
|
736
863
|
|
|
737
864
|
it('should handle boolean values', async () => {
|
|
738
|
-
const results = await vectorDB.query(
|
|
739
|
-
|
|
865
|
+
const results = await vectorDB.query({
|
|
866
|
+
indexName: testIndexName2,
|
|
867
|
+
queryVector: [1, 0, 0],
|
|
868
|
+
filter: { inStock: { $eq: false } },
|
|
740
869
|
});
|
|
741
870
|
expect(results.length).toBeGreaterThan(0);
|
|
742
871
|
results.forEach(result => {
|
|
@@ -748,46 +877,78 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
748
877
|
describe('Additional Validation Tests', () => {
|
|
749
878
|
it('should throw error as date is not supported', async () => {
|
|
750
879
|
await expect(
|
|
751
|
-
vectorDB.query(
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
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
|
+
},
|
|
756
889
|
}),
|
|
757
890
|
).rejects.toThrow();
|
|
758
891
|
});
|
|
759
892
|
it('should throw error as empty array in $in operator is not supported', async () => {
|
|
760
893
|
await expect(
|
|
761
|
-
vectorDB.query(
|
|
762
|
-
|
|
894
|
+
vectorDB.query({
|
|
895
|
+
indexName: testIndexName2,
|
|
896
|
+
queryVector: [1, 0, 0],
|
|
897
|
+
filter: {
|
|
898
|
+
category: { $in: [] },
|
|
899
|
+
},
|
|
763
900
|
}),
|
|
764
901
|
).rejects.toThrow();
|
|
765
902
|
});
|
|
766
903
|
it('should reject non-numeric values in numeric comparisons', async () => {
|
|
767
904
|
await expect(
|
|
768
|
-
vectorDB.query(
|
|
769
|
-
|
|
905
|
+
vectorDB.query({
|
|
906
|
+
indexName: testIndexName2,
|
|
907
|
+
queryVector: [1, 0, 0],
|
|
908
|
+
filter: {
|
|
909
|
+
price: { $gt: '500' }, // string instead of number
|
|
910
|
+
},
|
|
770
911
|
}),
|
|
771
912
|
).rejects.toThrow();
|
|
772
913
|
});
|
|
773
914
|
|
|
774
915
|
it('should reject mixed types in $in operator', async () => {
|
|
775
916
|
await expect(
|
|
776
|
-
vectorDB.query(
|
|
777
|
-
|
|
917
|
+
vectorDB.query({
|
|
918
|
+
indexName: testIndexName2,
|
|
919
|
+
queryVector: [1, 0, 0],
|
|
920
|
+
filter: {
|
|
921
|
+
field: { $in: ['string', 123] }, // mixed string and number
|
|
922
|
+
},
|
|
778
923
|
}),
|
|
779
924
|
).rejects.toThrow();
|
|
780
925
|
});
|
|
781
926
|
it('should handle undefined filter', async () => {
|
|
782
|
-
const results1 = await vectorDB.query(
|
|
783
|
-
|
|
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
|
+
});
|
|
784
937
|
expect(results1).toEqual(results2);
|
|
785
938
|
expect(results1.length).toBeGreaterThan(0);
|
|
786
939
|
});
|
|
787
940
|
|
|
788
941
|
it('should handle null filter', async () => {
|
|
789
|
-
const results = await vectorDB.query(
|
|
790
|
-
|
|
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
|
+
});
|
|
791
952
|
expect(results).toEqual(results2);
|
|
792
953
|
expect(results.length).toBeGreaterThan(0);
|
|
793
954
|
});
|
|
@@ -795,8 +956,12 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
795
956
|
|
|
796
957
|
describe('Additional Edge Cases', () => {
|
|
797
958
|
it('should handle exact boundary conditions', async () => {
|
|
798
|
-
const results = await vectorDB.query(
|
|
799
|
-
|
|
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
|
+
},
|
|
800
965
|
});
|
|
801
966
|
expect(results.length).toBeGreaterThan(0);
|
|
802
967
|
expect(results.some(r => r.metadata?.price === 25)).toBe(true);
|
|
@@ -806,15 +971,19 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
806
971
|
|
|
807
972
|
describe('Additional Complex Logical Combinations', () => {
|
|
808
973
|
it('should handle deeply nested $or conditions', async () => {
|
|
809
|
-
const results = await vectorDB.query(
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
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
|
+
},
|
|
818
987
|
});
|
|
819
988
|
expect(results.length).toBeGreaterThan(0);
|
|
820
989
|
results.forEach(result => {
|
|
@@ -827,8 +996,12 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
827
996
|
});
|
|
828
997
|
|
|
829
998
|
it('should handle multiple field comparisons with same value', async () => {
|
|
830
|
-
const results = await vectorDB.query(
|
|
831
|
-
|
|
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
|
+
},
|
|
832
1005
|
});
|
|
833
1006
|
expect(results.length).toBeGreaterThan(0);
|
|
834
1007
|
results.forEach(result => {
|
|
@@ -839,12 +1012,16 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
839
1012
|
|
|
840
1013
|
describe('Performance Edge Cases', () => {
|
|
841
1014
|
it('should handle filters with many conditions', async () => {
|
|
842
|
-
const results = await vectorDB.query(
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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
|
+
},
|
|
848
1025
|
});
|
|
849
1026
|
expect(results.length).toBeGreaterThan(0);
|
|
850
1027
|
results.forEach(result => {
|
|
@@ -853,12 +1030,20 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
853
1030
|
});
|
|
854
1031
|
|
|
855
1032
|
it('should handle deeply nested conditions efficiently', async () => {
|
|
856
|
-
const results = await vectorDB.query(
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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
|
+
},
|
|
862
1047
|
});
|
|
863
1048
|
expect(results.length).toBeGreaterThan(0);
|
|
864
1049
|
results.forEach(result => {
|
|
@@ -869,19 +1054,23 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
869
1054
|
});
|
|
870
1055
|
|
|
871
1056
|
it('should handle large number of $or conditions', async () => {
|
|
872
|
-
const results = await vectorDB.query(
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
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
|
+
},
|
|
885
1074
|
});
|
|
886
1075
|
expect(results.length).toBeGreaterThan(0);
|
|
887
1076
|
});
|
|
@@ -901,7 +1090,7 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
901
1090
|
} catch {
|
|
902
1091
|
// Ignore errors if index doesn't exist
|
|
903
1092
|
}
|
|
904
|
-
await vectorDB.createIndex(testIndexName3, dimension);
|
|
1093
|
+
await vectorDB.createIndex({ indexName: testIndexName3, dimension });
|
|
905
1094
|
|
|
906
1095
|
const testVectors = [
|
|
907
1096
|
[1.0, 0.0, 0.0],
|
|
@@ -916,7 +1105,13 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
916
1105
|
];
|
|
917
1106
|
const testIds = ['doc1', 'doc2', 'doc3'];
|
|
918
1107
|
|
|
919
|
-
await vectorDB.upsert(
|
|
1108
|
+
await vectorDB.upsert({
|
|
1109
|
+
indexName: testIndexName3,
|
|
1110
|
+
vectors: testVectors,
|
|
1111
|
+
documents: testDocuments,
|
|
1112
|
+
metadata: testMetadata,
|
|
1113
|
+
ids: testIds,
|
|
1114
|
+
});
|
|
920
1115
|
|
|
921
1116
|
// Wait for indexing
|
|
922
1117
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
@@ -933,29 +1128,39 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
933
1128
|
|
|
934
1129
|
describe('Basic Document Operations', () => {
|
|
935
1130
|
it('should store and retrieve documents', async () => {
|
|
936
|
-
const results = await vectorDB.query(testIndexName3, [1.0, 0.0, 0.0], 3);
|
|
1131
|
+
const results = await vectorDB.query({ indexName: testIndexName3, queryVector: [1.0, 0.0, 0.0], topK: 3 });
|
|
937
1132
|
expect(results).toHaveLength(3);
|
|
938
1133
|
// Verify documents are returned
|
|
939
1134
|
expect(results[0].document).toBe(testDocuments[0]);
|
|
940
1135
|
});
|
|
941
1136
|
|
|
942
1137
|
it('should filter documents using $contains', async () => {
|
|
943
|
-
const results = await vectorDB.query(
|
|
944
|
-
|
|
1138
|
+
const results = await vectorDB.query({
|
|
1139
|
+
indexName: testIndexName3,
|
|
1140
|
+
queryVector: [1.0, 0.0, 0.0],
|
|
1141
|
+
topK: 3,
|
|
1142
|
+
documentFilter: { $contains: 'quick' },
|
|
945
1143
|
});
|
|
946
1144
|
expect(results).toHaveLength(2);
|
|
947
1145
|
});
|
|
948
1146
|
|
|
949
1147
|
it('should filter with $not_contains', async () => {
|
|
950
|
-
const results = await vectorDB.query(
|
|
951
|
-
|
|
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' },
|
|
952
1153
|
});
|
|
953
1154
|
expect(results.every(r => !r.document?.includes('fox'))).toBe(true);
|
|
954
1155
|
});
|
|
955
1156
|
|
|
956
1157
|
it('should combine metadata and document filters', async () => {
|
|
957
|
-
const results = await vectorDB.query(
|
|
958
|
-
|
|
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' },
|
|
959
1164
|
});
|
|
960
1165
|
expect(results).toHaveLength(1);
|
|
961
1166
|
expect(results[0].metadata?.source).toBe('pangram1');
|
|
@@ -965,8 +1170,11 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
965
1170
|
|
|
966
1171
|
describe('Complex Document Filtering', () => {
|
|
967
1172
|
it('should handle $and conditions', async () => {
|
|
968
|
-
const results = await vectorDB.query(
|
|
969
|
-
|
|
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' }] },
|
|
970
1178
|
});
|
|
971
1179
|
expect(results).toHaveLength(1);
|
|
972
1180
|
expect(results[0].document).toContain('quick');
|
|
@@ -974,62 +1182,84 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
974
1182
|
});
|
|
975
1183
|
|
|
976
1184
|
it('should handle $or conditions', async () => {
|
|
977
|
-
const results = await vectorDB.query(
|
|
978
|
-
|
|
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' }] },
|
|
979
1190
|
});
|
|
980
1191
|
expect(results).toHaveLength(2);
|
|
1192
|
+
expect(results[0].document).toContain('fox');
|
|
1193
|
+
expect(results[1].document).toContain('zebras');
|
|
981
1194
|
});
|
|
982
1195
|
});
|
|
983
1196
|
|
|
984
1197
|
describe('Edge Cases and Validation', () => {
|
|
985
1198
|
it('should reject empty string in $contains', async () => {
|
|
986
1199
|
await expect(
|
|
987
|
-
vectorDB.query(
|
|
1200
|
+
vectorDB.query({
|
|
1201
|
+
indexName: testIndexName3,
|
|
1202
|
+
queryVector: [1.0, 0.0, 0.0],
|
|
1203
|
+
topK: 3,
|
|
1204
|
+
documentFilter: { $contains: '' },
|
|
1205
|
+
}),
|
|
988
1206
|
).rejects.toThrow('Expected where document operand value for operator $contains to be a non-empty str');
|
|
989
1207
|
});
|
|
990
1208
|
|
|
991
1209
|
it('should be case sensitive', async () => {
|
|
992
1210
|
// First verify lowercase works
|
|
993
|
-
const lowerResults = await vectorDB.query(
|
|
994
|
-
|
|
1211
|
+
const lowerResults = await vectorDB.query({
|
|
1212
|
+
indexName: testIndexName3,
|
|
1213
|
+
queryVector: [1.0, 0.0, 0.0],
|
|
1214
|
+
topK: 3,
|
|
1215
|
+
documentFilter: { $contains: 'quick' },
|
|
995
1216
|
});
|
|
996
1217
|
expect(lowerResults.length).toBe(2);
|
|
997
1218
|
|
|
998
1219
|
// Then verify uppercase doesn't match
|
|
999
|
-
const upperResults = await vectorDB.query(
|
|
1000
|
-
|
|
1220
|
+
const upperResults = await vectorDB.query({
|
|
1221
|
+
indexName: testIndexName3,
|
|
1222
|
+
queryVector: [1.0, 0.0, 0.0],
|
|
1223
|
+
topK: 3,
|
|
1224
|
+
documentFilter: { $contains: 'QUICK' },
|
|
1001
1225
|
});
|
|
1002
1226
|
expect(upperResults.length).toBe(0);
|
|
1003
1227
|
|
|
1004
|
-
const upperResults2 = await vectorDB.query(
|
|
1005
|
-
|
|
1228
|
+
const upperResults2 = await vectorDB.query({
|
|
1229
|
+
indexName: testIndexName3,
|
|
1230
|
+
queryVector: [1.0, 0.0, 0.0],
|
|
1231
|
+
topK: 3,
|
|
1232
|
+
documentFilter: { $contains: 'JUMP' },
|
|
1006
1233
|
});
|
|
1007
1234
|
expect(upperResults2.length).toBe(1);
|
|
1008
1235
|
});
|
|
1009
1236
|
|
|
1010
1237
|
it('should handle exact string matches', async () => {
|
|
1011
|
-
const results = await vectorDB.query(
|
|
1012
|
-
testIndexName3,
|
|
1013
|
-
[1.0, 0.0, 0.0],
|
|
1014
|
-
3,
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
{ $contains: 'quick brown' }, // Test multi-word match
|
|
1018
|
-
);
|
|
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
|
+
});
|
|
1019
1244
|
expect(results.length).toBe(1);
|
|
1020
1245
|
expect(results[0].document).toContain('quick brown');
|
|
1021
1246
|
});
|
|
1022
1247
|
|
|
1023
1248
|
it('should handle deeply nested logical operators', async () => {
|
|
1024
|
-
const results = await vectorDB.query(
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
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
|
+
},
|
|
1033
1263
|
});
|
|
1034
1264
|
expect(results.length).toBeGreaterThan(0);
|
|
1035
1265
|
results.forEach(result => {
|
|
@@ -1042,4 +1272,113 @@ describe('ChromaVector Integration Tests', () => {
|
|
|
1042
1272
|
});
|
|
1043
1273
|
});
|
|
1044
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' }],
|
|
1379
|
+
});
|
|
1380
|
+
expect(Array.isArray(upsertResults)).toBe(true);
|
|
1381
|
+
expect(upsertResults).toHaveLength(1);
|
|
1382
|
+
});
|
|
1383
|
+
});
|
|
1045
1384
|
});
|