@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.
- package/CHANGELOG.md +948 -3
- package/LICENSE.md +7 -0
- package/README.md +26 -16
- package/dist/_tsup-dts-rollup.d.cts +75 -0
- package/dist/_tsup-dts-rollup.d.ts +43 -11
- package/dist/index.cjs +307 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +124 -12
- package/docker-compose.yaml +7 -0
- package/eslint.config.js +6 -0
- package/package.json +18 -10
- package/src/index.ts +1 -0
- package/src/vector/filter.ts +5 -10
- package/src/vector/index.test.ts +909 -199
- package/src/vector/index.ts +98 -30
- package/src/vector/prompt.ts +72 -0
- package/.turbo/turbo-build.log +0 -19
- package/LICENSE +0 -44
package/src/vector/index.test.ts
CHANGED
|
@@ -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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
187
|
-
|
|
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(
|
|
193
|
-
|
|
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(
|
|
201
|
-
|
|
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(
|
|
209
|
-
|
|
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(
|
|
217
|
-
|
|
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(
|
|
223
|
-
|
|
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
|
|
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(
|
|
234
|
-
|
|
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(
|
|
248
|
-
|
|
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(
|
|
260
|
-
|
|
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(
|
|
272
|
-
|
|
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(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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(
|
|
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],
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
368
|
-
|
|
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(
|
|
378
|
-
|
|
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(
|
|
387
|
-
|
|
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(
|
|
397
|
-
|
|
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(
|
|
407
|
-
|
|
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(
|
|
417
|
-
|
|
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(
|
|
427
|
-
|
|
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(
|
|
438
|
-
|
|
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(
|
|
448
|
-
|
|
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(
|
|
458
|
-
|
|
459
|
-
|
|
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(
|
|
471
|
-
|
|
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(
|
|
481
|
-
|
|
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(
|
|
491
|
-
|
|
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(
|
|
503
|
-
|
|
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(
|
|
512
|
-
|
|
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(
|
|
524
|
-
|
|
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(
|
|
534
|
-
|
|
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(
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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(
|
|
564
|
-
|
|
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('
|
|
570
|
-
await
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
)
|
|
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
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
)
|
|
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(
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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(
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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(
|
|
626
|
-
|
|
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(
|
|
637
|
-
|
|
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(
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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(
|
|
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(
|
|
710
|
-
|
|
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(
|
|
721
|
-
|
|
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(
|
|
728
|
-
|
|
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(
|
|
738
|
-
|
|
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(
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
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(
|
|
761
|
-
|
|
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(
|
|
768
|
-
|
|
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(
|
|
776
|
-
|
|
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(
|
|
782
|
-
|
|
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(
|
|
789
|
-
|
|
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(
|
|
798
|
-
|
|
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(
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
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(
|
|
830
|
-
|
|
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(
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
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(
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
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(
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
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
|
});
|