@mastra/upstash 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 +25 -0
- package/README.md +20 -1
- package/dist/_tsup-dts-rollup.d.cts +133 -0
- package/dist/_tsup-dts-rollup.d.ts +12 -8
- package/dist/index.cjs +480 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.js +8 -4
- package/package.json +9 -5
- package/src/storage/upstash.test.ts +332 -330
- package/src/vector/filter.ts +5 -5
- package/src/vector/index.test.ts +440 -150
- package/src/vector/index.ts +20 -21
package/src/vector/index.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach, vi, afterEach } from 'vitest';
|
|
2
2
|
|
|
3
3
|
import { UpstashVector } from './';
|
|
4
4
|
|
|
@@ -84,12 +84,12 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
84
84
|
const testMetadata = [{ label: 'first-dimension' }, { label: 'second-dimension' }, { label: 'third-dimension' }];
|
|
85
85
|
|
|
86
86
|
// Upsert vectors
|
|
87
|
-
vectorIds = await vectorStore.upsert(
|
|
87
|
+
vectorIds = await vectorStore.upsert({ indexName: testIndexName, vectors: testVectors, metadata: testMetadata });
|
|
88
88
|
|
|
89
89
|
expect(vectorIds).toHaveLength(3);
|
|
90
90
|
await waitUntilVectorsIndexed(vectorStore, testIndexName, 3);
|
|
91
91
|
|
|
92
|
-
const results = await vectorStore.query(testIndexName, createVector(0, 0.9), 3);
|
|
92
|
+
const results = await vectorStore.query({ indexName: testIndexName, queryVector: createVector(0, 0.9), topK: 3 });
|
|
93
93
|
|
|
94
94
|
expect(results).toHaveLength(3);
|
|
95
95
|
if (results.length > 0) {
|
|
@@ -98,7 +98,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
98
98
|
}, 5000000);
|
|
99
99
|
|
|
100
100
|
it('should query vectors and return vector in results', async () => {
|
|
101
|
-
const results = await vectorStore.query(testIndexName, createVector(0, 0.9), 3
|
|
101
|
+
const results = await vectorStore.query({ indexName: testIndexName, queryVector: createVector(0, 0.9), topK: 3 });
|
|
102
102
|
expect(results).toHaveLength(3);
|
|
103
103
|
expect(results?.[0]?.vector).toBeDefined();
|
|
104
104
|
expect(results?.[0]?.vector).toHaveLength(VECTOR_DIMENSION);
|
|
@@ -110,7 +110,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
110
110
|
});
|
|
111
111
|
describe('Index Operations', () => {
|
|
112
112
|
it('should create and list an index', async () => {
|
|
113
|
-
await vectorStore.createIndex(testIndexName, 3, 'cosine');
|
|
113
|
+
await vectorStore.createIndex({ indexName: testIndexName, dimension: 3, metric: 'cosine' });
|
|
114
114
|
const indexes = await vectorStore.listIndexes();
|
|
115
115
|
expect(indexes).toEqual([testIndexName]);
|
|
116
116
|
});
|
|
@@ -128,13 +128,13 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
128
128
|
describe('Error Handling', () => {
|
|
129
129
|
it('should handle invalid dimension vectors', async () => {
|
|
130
130
|
await expect(
|
|
131
|
-
vectorStore.upsert(testIndexName, [[1.0, 0.0]]), // Wrong dimensions
|
|
131
|
+
vectorStore.upsert({ indexName: testIndexName, vectors: [[1.0, 0.0]] }), // Wrong dimensions
|
|
132
132
|
).rejects.toThrow();
|
|
133
133
|
});
|
|
134
134
|
|
|
135
135
|
it('should handle querying with wrong dimensions', async () => {
|
|
136
136
|
await expect(
|
|
137
|
-
vectorStore.query(testIndexName, [1.0, 0.0]), // Wrong dimensions
|
|
137
|
+
vectorStore.query({ indexName: testIndexName, queryVector: [1.0, 0.0] }), // Wrong dimensions
|
|
138
138
|
).rejects.toThrow();
|
|
139
139
|
});
|
|
140
140
|
});
|
|
@@ -219,26 +219,34 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
219
219
|
];
|
|
220
220
|
|
|
221
221
|
beforeAll(async () => {
|
|
222
|
-
await vectorStore.createIndex(filterIndexName, VECTOR_DIMENSION);
|
|
223
|
-
await vectorStore.upsert(
|
|
224
|
-
filterIndexName,
|
|
225
|
-
testData.map(d => d.vector),
|
|
226
|
-
testData.map(d => d.metadata),
|
|
227
|
-
testData.map(d => d.id),
|
|
228
|
-
);
|
|
222
|
+
await vectorStore.createIndex({ indexName: filterIndexName, dimension: VECTOR_DIMENSION });
|
|
223
|
+
await vectorStore.upsert({
|
|
224
|
+
indexName: filterIndexName,
|
|
225
|
+
vectors: testData.map(d => d.vector),
|
|
226
|
+
metadata: testData.map(d => d.metadata),
|
|
227
|
+
ids: testData.map(d => d.id),
|
|
228
|
+
});
|
|
229
229
|
// Wait for indexing
|
|
230
230
|
await waitUntilVectorsIndexed(vectorStore, filterIndexName, testData.length);
|
|
231
231
|
}, 50000);
|
|
232
232
|
|
|
233
233
|
describe('Basic Operators', () => {
|
|
234
234
|
it('should filter by exact match', async () => {
|
|
235
|
-
const results = await vectorStore.query(
|
|
235
|
+
const results = await vectorStore.query({
|
|
236
|
+
indexName: filterIndexName,
|
|
237
|
+
queryVector: createVector(0),
|
|
238
|
+
filter: { name: 'Istanbul' },
|
|
239
|
+
});
|
|
236
240
|
expect(results).toHaveLength(1);
|
|
237
241
|
expect(results[0]?.metadata?.name).toBe('Istanbul');
|
|
238
242
|
});
|
|
239
243
|
|
|
240
244
|
it('should filter by not equal', async () => {
|
|
241
|
-
const results = await vectorStore.query(
|
|
245
|
+
const results = await vectorStore.query({
|
|
246
|
+
indexName: filterIndexName,
|
|
247
|
+
queryVector: createVector(0),
|
|
248
|
+
filter: { name: { $ne: 'Berlin' } },
|
|
249
|
+
});
|
|
242
250
|
expect(results).toHaveLength(3);
|
|
243
251
|
results.forEach(result => {
|
|
244
252
|
expect(result.metadata?.name).not.toBe('Berlin');
|
|
@@ -246,7 +254,11 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
246
254
|
});
|
|
247
255
|
|
|
248
256
|
it('should filter by greater than', async () => {
|
|
249
|
-
const results = await vectorStore.query(
|
|
257
|
+
const results = await vectorStore.query({
|
|
258
|
+
indexName: filterIndexName,
|
|
259
|
+
queryVector: createVector(0),
|
|
260
|
+
filter: { population: { $gt: 1000000 } },
|
|
261
|
+
});
|
|
250
262
|
expect(results).toHaveLength(2);
|
|
251
263
|
results.forEach(result => {
|
|
252
264
|
expect(result.metadata?.population).toBeGreaterThan(1000000);
|
|
@@ -254,7 +266,11 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
254
266
|
});
|
|
255
267
|
|
|
256
268
|
it('should filter by less than or equal', async () => {
|
|
257
|
-
const results = await vectorStore.query(
|
|
269
|
+
const results = await vectorStore.query({
|
|
270
|
+
indexName: filterIndexName,
|
|
271
|
+
queryVector: createVector(0),
|
|
272
|
+
filter: { founded: { $lte: 1500 } },
|
|
273
|
+
});
|
|
258
274
|
expect(results).toHaveLength(2);
|
|
259
275
|
results.forEach(result => {
|
|
260
276
|
expect(result.metadata?.founded).toBeLessThanOrEqual(1500);
|
|
@@ -264,8 +280,11 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
264
280
|
|
|
265
281
|
describe('Array Operations', () => {
|
|
266
282
|
it('should filter by array contains', async () => {
|
|
267
|
-
const results = await vectorStore.query(
|
|
268
|
-
|
|
283
|
+
const results = await vectorStore.query({
|
|
284
|
+
indexName: filterIndexName,
|
|
285
|
+
queryVector: createVector(0),
|
|
286
|
+
topK: 10,
|
|
287
|
+
filter: { tags: { $contains: 'historic' } },
|
|
269
288
|
});
|
|
270
289
|
expect(results).toHaveLength(2);
|
|
271
290
|
results.forEach(result => {
|
|
@@ -274,8 +293,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
274
293
|
});
|
|
275
294
|
|
|
276
295
|
it('should filter by array not contains', async () => {
|
|
277
|
-
const results = await vectorStore.query(
|
|
278
|
-
|
|
296
|
+
const results = await vectorStore.query({
|
|
297
|
+
indexName: filterIndexName,
|
|
298
|
+
queryVector: createVector(0),
|
|
299
|
+
filter: { tags: { $not: { $contains: 'tech' } } },
|
|
279
300
|
});
|
|
280
301
|
expect(results).toHaveLength(3);
|
|
281
302
|
results.forEach(result => {
|
|
@@ -284,8 +305,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
284
305
|
});
|
|
285
306
|
|
|
286
307
|
it('should filter by in array', async () => {
|
|
287
|
-
const results = await vectorStore.query(
|
|
288
|
-
|
|
308
|
+
const results = await vectorStore.query({
|
|
309
|
+
indexName: filterIndexName,
|
|
310
|
+
queryVector: createVector(0),
|
|
311
|
+
filter: { 'location.continent': { $in: ['Asia', 'Europe'] } },
|
|
289
312
|
});
|
|
290
313
|
expect(results).toHaveLength(2);
|
|
291
314
|
results.forEach(result => {
|
|
@@ -294,8 +317,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
294
317
|
});
|
|
295
318
|
|
|
296
319
|
it('should filter by not in array', async () => {
|
|
297
|
-
const results = await vectorStore.query(
|
|
298
|
-
|
|
320
|
+
const results = await vectorStore.query({
|
|
321
|
+
indexName: filterIndexName,
|
|
322
|
+
queryVector: createVector(0),
|
|
323
|
+
filter: { name: { $nin: ['Berlin', 'Istanbul'] } },
|
|
299
324
|
});
|
|
300
325
|
expect(results).toHaveLength(2);
|
|
301
326
|
results.forEach(result => {
|
|
@@ -306,23 +331,30 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
306
331
|
|
|
307
332
|
describe('Array Indexing', () => {
|
|
308
333
|
it('should filter by first array element', async () => {
|
|
309
|
-
const results = await vectorStore.query(
|
|
334
|
+
const results = await vectorStore.query({
|
|
335
|
+
indexName: filterIndexName,
|
|
336
|
+
queryVector: createVector(0),
|
|
337
|
+
filter: { 'industries[0]': 'Tourism' },
|
|
338
|
+
});
|
|
310
339
|
expect(results).toHaveLength(1);
|
|
311
340
|
expect(results[0]?.metadata?.industries?.[0]).toBe('Tourism');
|
|
312
341
|
});
|
|
313
342
|
|
|
314
343
|
it('should filter by last array element', async () => {
|
|
315
|
-
const results = await vectorStore.query(
|
|
316
|
-
|
|
344
|
+
const results = await vectorStore.query({
|
|
345
|
+
indexName: filterIndexName,
|
|
346
|
+
queryVector: createVector(0),
|
|
347
|
+
filter: { 'industries[#-1]': 'Technology' },
|
|
317
348
|
});
|
|
318
349
|
expect(results).toHaveLength(1);
|
|
319
350
|
expect(results[0]?.metadata?.industries?.slice(-1)[0]).toBe('Technology');
|
|
320
351
|
});
|
|
321
352
|
|
|
322
353
|
it('should combine first and last element filters', async () => {
|
|
323
|
-
const results = await vectorStore.query(
|
|
324
|
-
|
|
325
|
-
|
|
354
|
+
const results = await vectorStore.query({
|
|
355
|
+
indexName: filterIndexName,
|
|
356
|
+
queryVector: createVector(0),
|
|
357
|
+
filter: { 'industries[0]': 'Tourism', 'tags[#-1]': 'metropolitan' },
|
|
326
358
|
});
|
|
327
359
|
expect(results).toHaveLength(1);
|
|
328
360
|
const result = results[0]?.metadata;
|
|
@@ -333,14 +365,20 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
333
365
|
|
|
334
366
|
describe('Nested Fields', () => {
|
|
335
367
|
it('should filter by nested field', async () => {
|
|
336
|
-
const results = await vectorStore.query(
|
|
368
|
+
const results = await vectorStore.query({
|
|
369
|
+
indexName: filterIndexName,
|
|
370
|
+
queryVector: createVector(0),
|
|
371
|
+
filter: { 'location.continent': 'Asia' },
|
|
372
|
+
});
|
|
337
373
|
expect(results).toHaveLength(1);
|
|
338
374
|
expect(results[0]?.metadata?.location?.continent).toBe('Asia');
|
|
339
375
|
});
|
|
340
376
|
|
|
341
377
|
it('should filter by deeply nested field with comparison', async () => {
|
|
342
|
-
const results = await vectorStore.query(
|
|
343
|
-
|
|
378
|
+
const results = await vectorStore.query({
|
|
379
|
+
indexName: filterIndexName,
|
|
380
|
+
queryVector: createVector(0),
|
|
381
|
+
filter: { 'location.coordinates.latitude': { $gt: 40 } },
|
|
344
382
|
});
|
|
345
383
|
expect(results).toHaveLength(2);
|
|
346
384
|
results.forEach(result => {
|
|
@@ -349,9 +387,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
349
387
|
});
|
|
350
388
|
|
|
351
389
|
it('should combine nested and array filters', async () => {
|
|
352
|
-
const results = await vectorStore.query(
|
|
353
|
-
|
|
354
|
-
|
|
390
|
+
const results = await vectorStore.query({
|
|
391
|
+
indexName: filterIndexName,
|
|
392
|
+
queryVector: createVector(0),
|
|
393
|
+
filter: { 'location.coordinates.latitude': { $gt: 40 }, 'industries[0]': 'Tourism' },
|
|
355
394
|
});
|
|
356
395
|
expect(results).toHaveLength(1);
|
|
357
396
|
const result = results[0]?.metadata;
|
|
@@ -362,8 +401,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
362
401
|
|
|
363
402
|
describe('Logical Operators', () => {
|
|
364
403
|
it('should combine conditions with AND', async () => {
|
|
365
|
-
const results = await vectorStore.query(
|
|
366
|
-
|
|
404
|
+
const results = await vectorStore.query({
|
|
405
|
+
indexName: filterIndexName,
|
|
406
|
+
queryVector: createVector(0),
|
|
407
|
+
filter: { $and: [{ population: { $gt: 1000000 } }, { isCapital: true }] },
|
|
367
408
|
});
|
|
368
409
|
expect(results).toHaveLength(1);
|
|
369
410
|
const result = results[0]?.metadata;
|
|
@@ -372,8 +413,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
372
413
|
});
|
|
373
414
|
|
|
374
415
|
it('should combine conditions with OR', async () => {
|
|
375
|
-
const results = await vectorStore.query(
|
|
376
|
-
|
|
416
|
+
const results = await vectorStore.query({
|
|
417
|
+
indexName: filterIndexName,
|
|
418
|
+
queryVector: createVector(0),
|
|
419
|
+
filter: { $or: [{ 'location.continent': 'Asia' }, { 'location.continent': 'Europe' }] },
|
|
377
420
|
});
|
|
378
421
|
expect(results).toHaveLength(2);
|
|
379
422
|
results.forEach(result => {
|
|
@@ -382,8 +425,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
382
425
|
});
|
|
383
426
|
|
|
384
427
|
it('should handle NOT operator', async () => {
|
|
385
|
-
const results = await vectorStore.query(
|
|
386
|
-
|
|
428
|
+
const results = await vectorStore.query({
|
|
429
|
+
indexName: filterIndexName,
|
|
430
|
+
queryVector: createVector(0),
|
|
431
|
+
filter: { $not: { isCapital: true } },
|
|
387
432
|
});
|
|
388
433
|
expect(results).toHaveLength(3);
|
|
389
434
|
results.forEach(result => {
|
|
@@ -392,8 +437,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
392
437
|
});
|
|
393
438
|
|
|
394
439
|
it('should handle NOT with comparison operators', async () => {
|
|
395
|
-
const results = await vectorStore.query(
|
|
396
|
-
|
|
440
|
+
const results = await vectorStore.query({
|
|
441
|
+
indexName: filterIndexName,
|
|
442
|
+
queryVector: createVector(0),
|
|
443
|
+
filter: { population: { $not: { $lt: 1000000 } } },
|
|
397
444
|
});
|
|
398
445
|
expect(results).toHaveLength(2);
|
|
399
446
|
results.forEach(result => {
|
|
@@ -402,8 +449,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
402
449
|
});
|
|
403
450
|
|
|
404
451
|
it('should handle NOT with contains operator', async () => {
|
|
405
|
-
const results = await vectorStore.query(
|
|
406
|
-
|
|
452
|
+
const results = await vectorStore.query({
|
|
453
|
+
indexName: filterIndexName,
|
|
454
|
+
queryVector: createVector(0),
|
|
455
|
+
filter: { tags: { $not: { $contains: 'tech' } } },
|
|
407
456
|
});
|
|
408
457
|
expect(results).toHaveLength(3);
|
|
409
458
|
results.forEach(result => {
|
|
@@ -412,8 +461,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
412
461
|
});
|
|
413
462
|
|
|
414
463
|
it('should handle NOT with regex operator', async () => {
|
|
415
|
-
const results = await vectorStore.query(
|
|
416
|
-
|
|
464
|
+
const results = await vectorStore.query({
|
|
465
|
+
indexName: filterIndexName,
|
|
466
|
+
queryVector: createVector(0),
|
|
467
|
+
filter: { name: { $not: { $regex: '*bul' } } },
|
|
417
468
|
});
|
|
418
469
|
expect(results).toHaveLength(3);
|
|
419
470
|
results.forEach(result => {
|
|
@@ -422,8 +473,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
422
473
|
});
|
|
423
474
|
|
|
424
475
|
it('should handle NOR operator', async () => {
|
|
425
|
-
const results = await vectorStore.query(
|
|
426
|
-
|
|
476
|
+
const results = await vectorStore.query({
|
|
477
|
+
indexName: filterIndexName,
|
|
478
|
+
queryVector: createVector(0),
|
|
479
|
+
filter: { $nor: [{ 'location.continent': 'Asia' }, { 'location.continent': 'Europe' }] },
|
|
427
480
|
});
|
|
428
481
|
expect(results).toHaveLength(1);
|
|
429
482
|
results.forEach(result => {
|
|
@@ -432,8 +485,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
432
485
|
});
|
|
433
486
|
|
|
434
487
|
it('should handle NOR with multiple conditions', async () => {
|
|
435
|
-
const results = await vectorStore.query(
|
|
436
|
-
|
|
488
|
+
const results = await vectorStore.query({
|
|
489
|
+
indexName: filterIndexName,
|
|
490
|
+
queryVector: createVector(0),
|
|
491
|
+
filter: { $nor: [{ population: { $gt: 10000000 } }, { isCapital: true }, { tags: { $contains: 'tech' } }] },
|
|
437
492
|
});
|
|
438
493
|
expect(results).toHaveLength(1);
|
|
439
494
|
const result = results[0]?.metadata;
|
|
@@ -443,8 +498,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
443
498
|
});
|
|
444
499
|
|
|
445
500
|
it('should handle ALL operator with simple values', async () => {
|
|
446
|
-
const results = await vectorStore.query(
|
|
447
|
-
|
|
501
|
+
const results = await vectorStore.query({
|
|
502
|
+
indexName: filterIndexName,
|
|
503
|
+
queryVector: createVector(0),
|
|
504
|
+
filter: { industries: { $all: ['Tourism', 'Finance'] } },
|
|
448
505
|
});
|
|
449
506
|
expect(results).toHaveLength(2);
|
|
450
507
|
results.forEach(result => {
|
|
@@ -454,17 +511,19 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
454
511
|
});
|
|
455
512
|
|
|
456
513
|
it('should handle ALL operator with empty array', async () => {
|
|
457
|
-
const results = await vectorStore.query(
|
|
458
|
-
|
|
514
|
+
const results = await vectorStore.query({
|
|
515
|
+
indexName: filterIndexName,
|
|
516
|
+
queryVector: createVector(0),
|
|
517
|
+
filter: { tags: { $all: [] } },
|
|
459
518
|
});
|
|
460
519
|
expect(results.length).toBeGreaterThan(0);
|
|
461
520
|
});
|
|
462
521
|
|
|
463
522
|
it('should handle NOT with nested logical operators', async () => {
|
|
464
|
-
const results = await vectorStore.query(
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
},
|
|
523
|
+
const results = await vectorStore.query({
|
|
524
|
+
indexName: filterIndexName,
|
|
525
|
+
queryVector: createVector(0),
|
|
526
|
+
filter: { $not: { $and: [{ population: { $lt: 1000000 } }, { isCapital: true }] } },
|
|
468
527
|
});
|
|
469
528
|
expect(results).toHaveLength(4);
|
|
470
529
|
results.forEach(result => {
|
|
@@ -474,8 +533,15 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
474
533
|
});
|
|
475
534
|
|
|
476
535
|
it('should handle NOR with nested path conditions', async () => {
|
|
477
|
-
const results = await vectorStore.query(
|
|
478
|
-
|
|
536
|
+
const results = await vectorStore.query({
|
|
537
|
+
indexName: filterIndexName,
|
|
538
|
+
queryVector: createVector(0),
|
|
539
|
+
filter: {
|
|
540
|
+
$nor: [
|
|
541
|
+
{ 'location.coordinates.latitude': { $lt: 40 } },
|
|
542
|
+
{ 'location.coordinates.longitude': { $gt: 100 } },
|
|
543
|
+
],
|
|
544
|
+
},
|
|
479
545
|
});
|
|
480
546
|
expect(results).toHaveLength(2);
|
|
481
547
|
results.forEach(result => {
|
|
@@ -485,11 +551,15 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
485
551
|
});
|
|
486
552
|
|
|
487
553
|
it('should handle exists with nested paths', async () => {
|
|
488
|
-
const results = await vectorStore.query(
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
554
|
+
const results = await vectorStore.query({
|
|
555
|
+
indexName: filterIndexName,
|
|
556
|
+
queryVector: createVector(0),
|
|
557
|
+
filter: {
|
|
558
|
+
$and: [
|
|
559
|
+
{ 'location.coordinates.latitude': { $exists: true } },
|
|
560
|
+
{ 'location.coordinates.longitude': { $exists: true } },
|
|
561
|
+
],
|
|
562
|
+
},
|
|
493
563
|
});
|
|
494
564
|
expect(results).toHaveLength(3);
|
|
495
565
|
results.forEach(result => {
|
|
@@ -499,9 +569,17 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
499
569
|
});
|
|
500
570
|
|
|
501
571
|
it('should handle complex NOT combinations', async () => {
|
|
502
|
-
const results = await vectorStore.query(
|
|
503
|
-
|
|
504
|
-
|
|
572
|
+
const results = await vectorStore.query({
|
|
573
|
+
indexName: filterIndexName,
|
|
574
|
+
queryVector: createVector(0),
|
|
575
|
+
filter: {
|
|
576
|
+
$not: {
|
|
577
|
+
$or: [
|
|
578
|
+
{ 'location.continent': 'Asia' },
|
|
579
|
+
{ population: { $lt: 1000000 } },
|
|
580
|
+
{ tags: { $contains: 'tech' } },
|
|
581
|
+
],
|
|
582
|
+
},
|
|
505
583
|
},
|
|
506
584
|
});
|
|
507
585
|
expect(results).toHaveLength(1);
|
|
@@ -512,20 +590,28 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
512
590
|
});
|
|
513
591
|
|
|
514
592
|
it('should handle NOR with regex patterns', async () => {
|
|
515
|
-
const results = await vectorStore.query(
|
|
516
|
-
|
|
593
|
+
const results = await vectorStore.query({
|
|
594
|
+
indexName: filterIndexName,
|
|
595
|
+
queryVector: createVector(0),
|
|
596
|
+
filter: {
|
|
597
|
+
$nor: [{ name: { $regex: '*bul' } }, { name: { $regex: '*lin' } }, { name: { $regex: '*cisco' } }],
|
|
598
|
+
},
|
|
517
599
|
});
|
|
518
600
|
expect(results).toHaveLength(1);
|
|
519
601
|
expect(results[0]?.metadata?.name).toBe("City's Name");
|
|
520
602
|
});
|
|
521
603
|
|
|
522
604
|
it('should handle NOR with mixed operator types', async () => {
|
|
523
|
-
const results = await vectorStore.query(
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
605
|
+
const results = await vectorStore.query({
|
|
606
|
+
indexName: filterIndexName,
|
|
607
|
+
queryVector: createVector(0),
|
|
608
|
+
filter: {
|
|
609
|
+
$nor: [
|
|
610
|
+
{ population: { $gt: 5000000 } },
|
|
611
|
+
{ tags: { $contains: 'tech' } },
|
|
612
|
+
{ 'location.coordinates.latitude': { $lt: 38 } },
|
|
613
|
+
],
|
|
614
|
+
},
|
|
529
615
|
});
|
|
530
616
|
expect(results).toHaveLength(1);
|
|
531
617
|
const result = results[0]?.metadata;
|
|
@@ -535,8 +621,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
535
621
|
});
|
|
536
622
|
|
|
537
623
|
it('should handle NOR with exists operator', async () => {
|
|
538
|
-
const results = await vectorStore.query(
|
|
539
|
-
|
|
624
|
+
const results = await vectorStore.query({
|
|
625
|
+
indexName: filterIndexName,
|
|
626
|
+
queryVector: createVector(0),
|
|
627
|
+
filter: { $nor: [{ lastCensus: { $exists: true } }, { population: { $exists: false } }] },
|
|
540
628
|
});
|
|
541
629
|
expect(results).toHaveLength(1);
|
|
542
630
|
const result = results[0]?.metadata;
|
|
@@ -545,8 +633,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
545
633
|
});
|
|
546
634
|
|
|
547
635
|
it('should handle ALL with mixed value types', async () => {
|
|
548
|
-
const results = await vectorStore.query(
|
|
549
|
-
|
|
636
|
+
const results = await vectorStore.query({
|
|
637
|
+
indexName: filterIndexName,
|
|
638
|
+
queryVector: createVector(0),
|
|
639
|
+
filter: { $and: [{ tags: { $contains: 'coastal' } }, { tags: { $contains: 'metropolitan' } }] },
|
|
550
640
|
});
|
|
551
641
|
expect(results).toHaveLength(2);
|
|
552
642
|
results.forEach(result => {
|
|
@@ -557,8 +647,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
557
647
|
});
|
|
558
648
|
|
|
559
649
|
it('should handle ALL with nested array conditions', async () => {
|
|
560
|
-
const results = await vectorStore.query(
|
|
561
|
-
|
|
650
|
+
const results = await vectorStore.query({
|
|
651
|
+
indexName: filterIndexName,
|
|
652
|
+
queryVector: createVector(0),
|
|
653
|
+
filter: { $and: [{ industries: { $all: ['Tourism', 'Finance'] } }, { tags: { $all: ['metropolitan'] } }] },
|
|
562
654
|
});
|
|
563
655
|
expect(results).toHaveLength(2);
|
|
564
656
|
results.forEach(result => {
|
|
@@ -569,8 +661,12 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
569
661
|
});
|
|
570
662
|
|
|
571
663
|
it('should handle ALL with complex conditions', async () => {
|
|
572
|
-
const results = await vectorStore.query(
|
|
573
|
-
|
|
664
|
+
const results = await vectorStore.query({
|
|
665
|
+
indexName: filterIndexName,
|
|
666
|
+
queryVector: createVector(0),
|
|
667
|
+
filter: {
|
|
668
|
+
$or: [{ industries: { $all: ['Tourism', 'Finance'] } }, { tags: { $all: ['tech', 'metropolitan'] } }],
|
|
669
|
+
},
|
|
574
670
|
});
|
|
575
671
|
expect(results).toHaveLength(2);
|
|
576
672
|
results.forEach(result => {
|
|
@@ -582,8 +678,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
582
678
|
});
|
|
583
679
|
|
|
584
680
|
it('should handle ALL with single item array', async () => {
|
|
585
|
-
const results = await vectorStore.query(
|
|
586
|
-
|
|
681
|
+
const results = await vectorStore.query({
|
|
682
|
+
indexName: filterIndexName,
|
|
683
|
+
queryVector: createVector(0),
|
|
684
|
+
filter: { industries: { $all: ['Technology'] } },
|
|
587
685
|
});
|
|
588
686
|
expect(results).toHaveLength(3);
|
|
589
687
|
results.forEach(result => {
|
|
@@ -592,13 +690,17 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
592
690
|
});
|
|
593
691
|
|
|
594
692
|
it('should handle complex nested conditions', async () => {
|
|
595
|
-
const results = await vectorStore.query(
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
693
|
+
const results = await vectorStore.query({
|
|
694
|
+
indexName: filterIndexName,
|
|
695
|
+
queryVector: createVector(0),
|
|
696
|
+
filter: {
|
|
697
|
+
$and: [
|
|
698
|
+
{ population: { $gt: 1000000 } },
|
|
699
|
+
{
|
|
700
|
+
$or: [{ 'location.continent': 'Asia' }, { industries: { $contains: 'Technology' } }],
|
|
701
|
+
},
|
|
702
|
+
],
|
|
703
|
+
},
|
|
602
704
|
});
|
|
603
705
|
expect(results).toHaveLength(2);
|
|
604
706
|
results.forEach(result => {
|
|
@@ -612,34 +714,58 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
612
714
|
describe('Edge Cases', () => {
|
|
613
715
|
describe('Empty Conditions', () => {
|
|
614
716
|
it('should handle empty AND array', async () => {
|
|
615
|
-
const results = await vectorStore.query(
|
|
717
|
+
const results = await vectorStore.query({
|
|
718
|
+
indexName: filterIndexName,
|
|
719
|
+
queryVector: createVector(0),
|
|
720
|
+
filter: { $and: [] },
|
|
721
|
+
});
|
|
616
722
|
expect(results.length).toBeGreaterThan(0);
|
|
617
723
|
});
|
|
618
724
|
|
|
619
725
|
it('should handle empty OR array', async () => {
|
|
620
|
-
const results = await vectorStore.query(
|
|
726
|
+
const results = await vectorStore.query({
|
|
727
|
+
indexName: filterIndexName,
|
|
728
|
+
queryVector: createVector(0),
|
|
729
|
+
filter: { $or: [] },
|
|
730
|
+
});
|
|
621
731
|
expect(results.length).toBe(0);
|
|
622
732
|
});
|
|
623
733
|
|
|
624
734
|
it('should handle empty IN array', async () => {
|
|
625
|
-
const results = await vectorStore.query(
|
|
735
|
+
const results = await vectorStore.query({
|
|
736
|
+
indexName: filterIndexName,
|
|
737
|
+
queryVector: createVector(0),
|
|
738
|
+
filter: { tags: { $in: [] } },
|
|
739
|
+
});
|
|
626
740
|
expect(results.length).toBe(0);
|
|
627
741
|
});
|
|
628
742
|
it('should handle empty IN array', async () => {
|
|
629
|
-
const results = await vectorStore.query(
|
|
743
|
+
const results = await vectorStore.query({
|
|
744
|
+
indexName: filterIndexName,
|
|
745
|
+
queryVector: createVector(0),
|
|
746
|
+
filter: { tags: [] },
|
|
747
|
+
});
|
|
630
748
|
expect(results.length).toBe(0);
|
|
631
749
|
});
|
|
632
750
|
});
|
|
633
751
|
|
|
634
752
|
describe('Null/Undefined Values', () => {
|
|
635
753
|
it('should handle null values', async () => {
|
|
636
|
-
await expect(
|
|
754
|
+
await expect(
|
|
755
|
+
vectorStore.query({
|
|
756
|
+
indexName: filterIndexName,
|
|
757
|
+
queryVector: createVector(0),
|
|
758
|
+
filter: { lastCensus: null },
|
|
759
|
+
}),
|
|
760
|
+
).rejects.toThrow();
|
|
637
761
|
});
|
|
638
762
|
|
|
639
763
|
it('should handle null in arrays', async () => {
|
|
640
764
|
await expect(
|
|
641
|
-
vectorStore.query(
|
|
642
|
-
|
|
765
|
+
vectorStore.query({
|
|
766
|
+
indexName: filterIndexName,
|
|
767
|
+
queryVector: createVector(0),
|
|
768
|
+
filter: { tags: { $in: [null, 'historic'] } },
|
|
643
769
|
}),
|
|
644
770
|
).rejects.toThrow();
|
|
645
771
|
});
|
|
@@ -647,16 +773,20 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
647
773
|
|
|
648
774
|
describe('Special Characters', () => {
|
|
649
775
|
it('should handle strings with quotes', async () => {
|
|
650
|
-
const results = await vectorStore.query(
|
|
651
|
-
|
|
776
|
+
const results = await vectorStore.query({
|
|
777
|
+
indexName: filterIndexName,
|
|
778
|
+
queryVector: createVector(0),
|
|
779
|
+
filter: { name: "City's Name" },
|
|
652
780
|
});
|
|
653
781
|
expect(results).toHaveLength(1);
|
|
654
782
|
expect(results[0]?.metadata?.name).toBe("City's Name");
|
|
655
783
|
});
|
|
656
784
|
|
|
657
785
|
it('should handle strings with double quotes', async () => {
|
|
658
|
-
const results = await vectorStore.query(
|
|
659
|
-
|
|
786
|
+
const results = await vectorStore.query({
|
|
787
|
+
indexName: filterIndexName,
|
|
788
|
+
queryVector: createVector(0),
|
|
789
|
+
filter: { description: 'Contains "quotes"' },
|
|
660
790
|
});
|
|
661
791
|
expect(results).toHaveLength(1);
|
|
662
792
|
expect(results[0]?.metadata?.description).toBe('Contains "quotes"');
|
|
@@ -665,61 +795,92 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
665
795
|
|
|
666
796
|
describe('Number Formats', () => {
|
|
667
797
|
it('should handle zero', async () => {
|
|
668
|
-
const results = await vectorStore.query(
|
|
669
|
-
|
|
798
|
+
const results = await vectorStore.query({
|
|
799
|
+
indexName: filterIndexName,
|
|
800
|
+
queryVector: createVector(0),
|
|
801
|
+
filter: { population: 0 },
|
|
670
802
|
});
|
|
671
803
|
expect(results).toHaveLength(1);
|
|
672
804
|
expect(results[0]?.metadata?.population).toBe(0);
|
|
673
805
|
});
|
|
674
806
|
|
|
675
807
|
it('should handle negative numbers', async () => {
|
|
676
|
-
const results = await vectorStore.query(
|
|
677
|
-
|
|
808
|
+
const results = await vectorStore.query({
|
|
809
|
+
indexName: filterIndexName,
|
|
810
|
+
queryVector: createVector(0),
|
|
811
|
+
filter: { temperature: -10 },
|
|
678
812
|
});
|
|
679
813
|
expect(results).toHaveLength(1);
|
|
680
814
|
expect(results[0]?.metadata?.temperature).toBe(-10);
|
|
681
815
|
});
|
|
682
816
|
|
|
683
817
|
it('should handle decimal numbers', async () => {
|
|
684
|
-
const results = await vectorStore.query(
|
|
685
|
-
|
|
818
|
+
const results = await vectorStore.query({
|
|
819
|
+
indexName: filterIndexName,
|
|
820
|
+
queryVector: createVector(0),
|
|
821
|
+
filter: { 'location.coordinates.latitude': 41.0082 },
|
|
686
822
|
});
|
|
687
823
|
expect(results).toHaveLength(1);
|
|
688
824
|
expect(results[0]?.metadata?.location?.coordinates?.latitude).toBe(41.0082);
|
|
689
825
|
});
|
|
690
826
|
|
|
691
827
|
it('should handle scientific notation', async () => {
|
|
692
|
-
const results = await vectorStore.query(
|
|
693
|
-
|
|
828
|
+
const results = await vectorStore.query({
|
|
829
|
+
indexName: filterIndexName,
|
|
830
|
+
queryVector: createVector(0),
|
|
831
|
+
filter: { microscopicDetail: 1e-10 },
|
|
694
832
|
});
|
|
695
833
|
expect(results).toHaveLength(1);
|
|
696
834
|
expect(results[0]?.metadata?.microscopicDetail).toBe(1e-10);
|
|
697
835
|
});
|
|
698
836
|
|
|
699
837
|
it('should handle escaped quotes in strings', async () => {
|
|
700
|
-
const results = await vectorStore.query(
|
|
701
|
-
|
|
838
|
+
const results = await vectorStore.query({
|
|
839
|
+
indexName: filterIndexName,
|
|
840
|
+
queryVector: createVector(0),
|
|
841
|
+
filter: { description: { $regex: '*"quotes"*' } },
|
|
702
842
|
});
|
|
703
843
|
expect(results).toHaveLength(1);
|
|
704
844
|
expect(results[0]?.metadata?.description).toBe('Contains "quotes"');
|
|
705
845
|
});
|
|
706
846
|
it('should handle undefined filter', async () => {
|
|
707
|
-
const results1 = await vectorStore.query(
|
|
708
|
-
|
|
847
|
+
const results1 = await vectorStore.query({
|
|
848
|
+
indexName: filterIndexName,
|
|
849
|
+
queryVector: createVector(0),
|
|
850
|
+
filter: undefined,
|
|
851
|
+
});
|
|
852
|
+
const results2 = await vectorStore.query({
|
|
853
|
+
indexName: filterIndexName,
|
|
854
|
+
queryVector: createVector(0),
|
|
855
|
+
});
|
|
709
856
|
expect(results1).toEqual(results2);
|
|
710
857
|
expect(results1.length).toBeGreaterThan(0);
|
|
711
858
|
});
|
|
712
859
|
|
|
713
860
|
it('should handle empty object filter', async () => {
|
|
714
|
-
const results = await vectorStore.query(
|
|
715
|
-
|
|
861
|
+
const results = await vectorStore.query({
|
|
862
|
+
indexName: filterIndexName,
|
|
863
|
+
queryVector: createVector(0),
|
|
864
|
+
filter: {},
|
|
865
|
+
});
|
|
866
|
+
const results2 = await vectorStore.query({
|
|
867
|
+
indexName: filterIndexName,
|
|
868
|
+
queryVector: createVector(0),
|
|
869
|
+
});
|
|
716
870
|
expect(results).toEqual(results2);
|
|
717
871
|
expect(results.length).toBeGreaterThan(0);
|
|
718
872
|
});
|
|
719
873
|
|
|
720
874
|
it('should handle null filter', async () => {
|
|
721
|
-
const results = await vectorStore.query(
|
|
722
|
-
|
|
875
|
+
const results = await vectorStore.query({
|
|
876
|
+
indexName: filterIndexName,
|
|
877
|
+
queryVector: createVector(0),
|
|
878
|
+
filter: null,
|
|
879
|
+
});
|
|
880
|
+
const results2 = await vectorStore.query({
|
|
881
|
+
indexName: filterIndexName,
|
|
882
|
+
queryVector: createVector(0),
|
|
883
|
+
});
|
|
723
884
|
expect(results).toEqual(results2);
|
|
724
885
|
expect(results.length).toBeGreaterThan(0);
|
|
725
886
|
});
|
|
@@ -728,24 +889,30 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
728
889
|
|
|
729
890
|
describe('Pattern Matching', () => {
|
|
730
891
|
it('should match start of string', async () => {
|
|
731
|
-
const results = await vectorStore.query(
|
|
732
|
-
|
|
892
|
+
const results = await vectorStore.query({
|
|
893
|
+
indexName: filterIndexName,
|
|
894
|
+
queryVector: createVector(0),
|
|
895
|
+
filter: { name: { $regex: 'San*' } },
|
|
733
896
|
});
|
|
734
897
|
expect(results).toHaveLength(1);
|
|
735
898
|
expect(results[0]?.metadata?.name).toBe('San Francisco');
|
|
736
899
|
});
|
|
737
900
|
|
|
738
901
|
it('should match end of string', async () => {
|
|
739
|
-
const results = await vectorStore.query(
|
|
740
|
-
|
|
902
|
+
const results = await vectorStore.query({
|
|
903
|
+
indexName: filterIndexName,
|
|
904
|
+
queryVector: createVector(0),
|
|
905
|
+
filter: { name: { $regex: '*in' } },
|
|
741
906
|
});
|
|
742
907
|
expect(results).toHaveLength(1);
|
|
743
908
|
expect(results[0]?.metadata?.name).toBe('Berlin');
|
|
744
909
|
});
|
|
745
910
|
|
|
746
911
|
it('should handle negated pattern', async () => {
|
|
747
|
-
const results = await vectorStore.query(
|
|
748
|
-
|
|
912
|
+
const results = await vectorStore.query({
|
|
913
|
+
indexName: filterIndexName,
|
|
914
|
+
queryVector: createVector(0),
|
|
915
|
+
filter: { name: { $not: { $regex: 'A*' } } },
|
|
749
916
|
});
|
|
750
917
|
expect(results).toHaveLength(4);
|
|
751
918
|
});
|
|
@@ -753,15 +920,19 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
753
920
|
|
|
754
921
|
describe('Field Existence', () => {
|
|
755
922
|
it('should check field exists', async () => {
|
|
756
|
-
const results = await vectorStore.query(
|
|
757
|
-
|
|
923
|
+
const results = await vectorStore.query({
|
|
924
|
+
indexName: filterIndexName,
|
|
925
|
+
queryVector: createVector(0),
|
|
926
|
+
filter: { 'location.coordinates': { $exists: true } },
|
|
758
927
|
});
|
|
759
928
|
expect(results).toHaveLength(3);
|
|
760
929
|
});
|
|
761
930
|
|
|
762
931
|
it('should check field does not exist', async () => {
|
|
763
|
-
const results = await vectorStore.query(
|
|
764
|
-
|
|
932
|
+
const results = await vectorStore.query({
|
|
933
|
+
indexName: filterIndexName,
|
|
934
|
+
queryVector: createVector(0),
|
|
935
|
+
filter: { unknownField: { $exists: false } },
|
|
765
936
|
});
|
|
766
937
|
expect(results).toHaveLength(4);
|
|
767
938
|
});
|
|
@@ -771,8 +942,10 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
771
942
|
it('should reject large arrays', async () => {
|
|
772
943
|
const largeArray = Array.from({ length: 1000 }, (_, i) => `value${i}`);
|
|
773
944
|
await expect(
|
|
774
|
-
vectorStore.query(
|
|
775
|
-
|
|
945
|
+
vectorStore.query({
|
|
946
|
+
indexName: filterIndexName,
|
|
947
|
+
queryVector: createVector(0),
|
|
948
|
+
filter: { tags: { $in: largeArray } },
|
|
776
949
|
}),
|
|
777
950
|
).rejects.toThrow();
|
|
778
951
|
});
|
|
@@ -792,7 +965,11 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
792
965
|
],
|
|
793
966
|
};
|
|
794
967
|
const start = Date.now();
|
|
795
|
-
const results = await vectorStore.query(
|
|
968
|
+
const results = await vectorStore.query({
|
|
969
|
+
indexName: filterIndexName,
|
|
970
|
+
queryVector: createVector(0),
|
|
971
|
+
filter: deepFilter,
|
|
972
|
+
});
|
|
796
973
|
const duration = Date.now() - start;
|
|
797
974
|
expect(duration).toBeLessThan(1000);
|
|
798
975
|
expect(Array.isArray(results)).toBe(true);
|
|
@@ -811,7 +988,11 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
811
988
|
})),
|
|
812
989
|
};
|
|
813
990
|
const start = Date.now();
|
|
814
|
-
const results = await vectorStore.query(
|
|
991
|
+
const results = await vectorStore.query({
|
|
992
|
+
indexName: filterIndexName,
|
|
993
|
+
queryVector: createVector(0),
|
|
994
|
+
filter: complexFilter,
|
|
995
|
+
});
|
|
815
996
|
const duration = Date.now() - start;
|
|
816
997
|
expect(duration).toBeLessThan(1000);
|
|
817
998
|
expect(Array.isArray(results)).toBe(true);
|
|
@@ -821,39 +1002,49 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
821
1002
|
describe('Error Cases', () => {
|
|
822
1003
|
it('should reject invalid operators', async () => {
|
|
823
1004
|
await expect(
|
|
824
|
-
vectorStore.query(
|
|
825
|
-
|
|
1005
|
+
vectorStore.query({
|
|
1006
|
+
indexName: filterIndexName,
|
|
1007
|
+
queryVector: createVector(0),
|
|
1008
|
+
filter: { field: { $invalidOp: 'value' } },
|
|
826
1009
|
}),
|
|
827
1010
|
).rejects.toThrow();
|
|
828
1011
|
});
|
|
829
1012
|
|
|
830
1013
|
it('should reject empty brackets', async () => {
|
|
831
1014
|
await expect(
|
|
832
|
-
vectorStore.query(
|
|
833
|
-
|
|
1015
|
+
vectorStore.query({
|
|
1016
|
+
indexName: filterIndexName,
|
|
1017
|
+
queryVector: createVector(0),
|
|
1018
|
+
filter: { 'industries[]': 'Tourism' },
|
|
834
1019
|
}),
|
|
835
1020
|
).rejects.toThrow();
|
|
836
1021
|
});
|
|
837
1022
|
|
|
838
1023
|
it('should reject unclosed brackets', async () => {
|
|
839
1024
|
await expect(
|
|
840
|
-
vectorStore.query(
|
|
841
|
-
|
|
1025
|
+
vectorStore.query({
|
|
1026
|
+
indexName: filterIndexName,
|
|
1027
|
+
queryVector: createVector(0),
|
|
1028
|
+
filter: { 'industries[': 'Tourism' },
|
|
842
1029
|
}),
|
|
843
1030
|
).rejects.toThrow();
|
|
844
1031
|
});
|
|
845
1032
|
|
|
846
1033
|
it('should handle invalid array syntax by returning empty results', async () => {
|
|
847
|
-
const results = await vectorStore.query(
|
|
848
|
-
|
|
1034
|
+
const results = await vectorStore.query({
|
|
1035
|
+
indexName: filterIndexName,
|
|
1036
|
+
queryVector: createVector(0),
|
|
1037
|
+
filter: { 'industries#-1]': 'Tourism' },
|
|
849
1038
|
});
|
|
850
1039
|
expect(results).toHaveLength(0);
|
|
851
1040
|
});
|
|
852
1041
|
|
|
853
1042
|
it('should reject invalid field paths', async () => {
|
|
854
1043
|
await expect(
|
|
855
|
-
vectorStore.query(
|
|
856
|
-
|
|
1044
|
+
vectorStore.query({
|
|
1045
|
+
indexName: filterIndexName,
|
|
1046
|
+
queryVector: createVector(0),
|
|
1047
|
+
filter: { '.invalidPath': 'value' },
|
|
857
1048
|
}),
|
|
858
1049
|
).rejects.toThrow();
|
|
859
1050
|
});
|
|
@@ -861,11 +1052,110 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
861
1052
|
it('should handle malformed complex queries by returning all results', async () => {
|
|
862
1053
|
// Upstash treats malformed logical operators as non-filtering conditions
|
|
863
1054
|
// rather than throwing errors
|
|
864
|
-
const results = await vectorStore.query(
|
|
865
|
-
|
|
1055
|
+
const results = await vectorStore.query({
|
|
1056
|
+
indexName: filterIndexName,
|
|
1057
|
+
queryVector: createVector(0),
|
|
1058
|
+
filter: { $and: { not: 'an array' } },
|
|
866
1059
|
});
|
|
867
1060
|
expect(results.length).toBeGreaterThan(0);
|
|
868
1061
|
});
|
|
869
1062
|
});
|
|
870
1063
|
});
|
|
1064
|
+
describe('Deprecation Warnings', () => {
|
|
1065
|
+
const indexName = 'testdeprecationwarnings';
|
|
1066
|
+
|
|
1067
|
+
const indexName2 = 'testdeprecationwarnings2';
|
|
1068
|
+
|
|
1069
|
+
let warnSpy;
|
|
1070
|
+
|
|
1071
|
+
beforeAll(async () => {
|
|
1072
|
+
await vectorStore.createIndex({ indexName: indexName, dimension: 3 });
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
afterAll(async () => {
|
|
1076
|
+
await vectorStore.deleteIndex(indexName);
|
|
1077
|
+
await vectorStore.deleteIndex(indexName2);
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
beforeEach(async () => {
|
|
1081
|
+
warnSpy = vi.spyOn(vectorStore['logger'], 'warn');
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
afterEach(async () => {
|
|
1085
|
+
warnSpy.mockRestore();
|
|
1086
|
+
await vectorStore.deleteIndex(indexName2);
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
it('should show deprecation warning when using individual args for createIndex', async () => {
|
|
1090
|
+
await vectorStore.createIndex(indexName2, 3, 'cosine');
|
|
1091
|
+
|
|
1092
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
1093
|
+
expect.stringContaining('Deprecation Warning: Passing individual arguments to createIndex() is deprecated'),
|
|
1094
|
+
);
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
it('should show deprecation warning when using individual args for upsert', async () => {
|
|
1098
|
+
await vectorStore.upsert(indexName, [[1, 2, 3]], [{ test: 'data' }]);
|
|
1099
|
+
|
|
1100
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
1101
|
+
expect.stringContaining('Deprecation Warning: Passing individual arguments to upsert() is deprecated'),
|
|
1102
|
+
);
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
it('should show deprecation warning when using individual args for query', async () => {
|
|
1106
|
+
await vectorStore.query(indexName, [1, 2, 3], 5);
|
|
1107
|
+
|
|
1108
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
1109
|
+
expect.stringContaining('Deprecation Warning: Passing individual arguments to query() is deprecated'),
|
|
1110
|
+
);
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
it('should not show deprecation warning when using object param for query', async () => {
|
|
1114
|
+
await vectorStore.query({
|
|
1115
|
+
indexName,
|
|
1116
|
+
queryVector: [1, 2, 3],
|
|
1117
|
+
topK: 5,
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
it('should not show deprecation warning when using object param for createIndex', async () => {
|
|
1124
|
+
await vectorStore.createIndex({
|
|
1125
|
+
indexName: indexName2,
|
|
1126
|
+
dimension: 3,
|
|
1127
|
+
metric: 'cosine',
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
it('should not show deprecation warning when using object param for upsert', async () => {
|
|
1134
|
+
await vectorStore.upsert({
|
|
1135
|
+
indexName,
|
|
1136
|
+
vectors: [[1, 2, 3]],
|
|
1137
|
+
metadata: [{ test: 'data' }],
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
it('should maintain backward compatibility with individual args', async () => {
|
|
1144
|
+
// Query
|
|
1145
|
+
const queryResults = await vectorStore.query(indexName, [1, 2, 3], 5);
|
|
1146
|
+
expect(Array.isArray(queryResults)).toBe(true);
|
|
1147
|
+
|
|
1148
|
+
// CreateIndex
|
|
1149
|
+
await expect(vectorStore.createIndex(indexName2, 3, 'cosine')).resolves.not.toThrow();
|
|
1150
|
+
|
|
1151
|
+
// Upsert
|
|
1152
|
+
const upsertResults = await vectorStore.upsert({
|
|
1153
|
+
indexName,
|
|
1154
|
+
vectors: [[1, 2, 3]],
|
|
1155
|
+
metadata: [{ test: 'data' }],
|
|
1156
|
+
});
|
|
1157
|
+
expect(Array.isArray(upsertResults)).toBe(true);
|
|
1158
|
+
expect(upsertResults).toHaveLength(1);
|
|
1159
|
+
});
|
|
1160
|
+
});
|
|
871
1161
|
});
|