From 767ce00416debdec7d71431433617dc7085aa57b Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Wed, 29 May 2024 11:39:33 +0200 Subject: [PATCH 01/14] add upsert, query, range, delete, fetch tests with client --- src/commands/client/delete/index.test.ts | 30 +++++++ src/commands/client/fetch/index.test.ts | 39 ++++++--- src/commands/client/query/index.test.ts | 106 +++++++++++++++++++++++ src/commands/client/range/index.test.ts | 37 +++++++- src/commands/client/update/index.test.ts | 33 ++++++- src/commands/client/upsert/index.test.ts | 71 +++++++++++++-- src/utils/test-utils.ts | 1 + 7 files changed, 299 insertions(+), 18 deletions(-) diff --git a/src/commands/client/delete/index.test.ts b/src/commands/client/delete/index.test.ts index 845fd02..855a22e 100644 --- a/src/commands/client/delete/index.test.ts +++ b/src/commands/client/delete/index.test.ts @@ -1,6 +1,7 @@ import { afterAll, describe, expect, test } from "bun:test"; import { DeleteCommand, UpsertCommand } from "@commands/index"; import { newHttpClient, randomID, range, resetIndexes } from "@utils/test-utils"; +import { Index } from "@utils/test-utils"; const client = newHttpClient(); @@ -47,3 +48,32 @@ describe("DELETE", () => { }); }); }); + +describe("DELETE with Index Client", () => { + const index = new Index({ + token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + }); + + test("should delete single record succesfully", () => { + const initialVector = range(0, 384); + const id = randomID(); + + index.upsert({ id, vector: initialVector }); + + const deletionResult = index.delete(id); + expect(deletionResult).toBeTruthy(); + }); + + test("should delete array of records successfully", async () => { + const initialVector = range(0, 384); + const idsToUpsert = [randomID(), randomID(), randomID()]; + + const upsertPromises = idsToUpsert.map((id) => index.upsert({ id, vector: initialVector })); + + await Promise.all(upsertPromises); + + const deletionResult = await index.delete(idsToUpsert); + expect(deletionResult).toBeTruthy(); + }); +}); diff --git a/src/commands/client/fetch/index.test.ts b/src/commands/client/fetch/index.test.ts index ebdd0b5..bae5448 100644 --- a/src/commands/client/fetch/index.test.ts +++ b/src/commands/client/fetch/index.test.ts @@ -55,20 +55,30 @@ describe("FETCH", () => { expect(res).toEqual([mockData]); }); +}); - test("should fetch succesfully by index.fetch", async () => { - const index = new Index({ - url: process.env.UPSTASH_VECTOR_REST_URL!, - token: process.env.UPSTASH_VECTOR_REST_TOKEN!, - }); +describe("FETCH with Index Client", () => { + const index = new Index({ + token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + }); - const randomFetch = await index.fetch([randomID()], { - includeMetadata: true, - namespace: "test", - }); + test("should fetch array of records by IDs succesfully", async () => { + const randomizedData = new Array(20) + .fill("") + .map(() => ({ id: randomID(), vector: range(0, 384) })); - expect(randomFetch).toEqual([null]); + await index.upsert(randomizedData); + + await awaitUntilIndexed(index); + const IDs = randomizedData.map((x) => x.id); + const res = await index.fetch(IDs, { includeVectors: true }); + + expect(res).toEqual(randomizedData); + }); + + test("should fetch single record by ID", async () => { const mockData = { id: randomID(), vector: range(0, 384), @@ -87,4 +97,13 @@ describe("FETCH", () => { expect(fetchWithID).toEqual([mockData]); }); + + test("should return null when ID does not exist", async () => { + const randomFetch = await index.fetch([randomID()], { + includeMetadata: true, + namespace: "test", + }); + + expect(randomFetch).toEqual([null]); + }); }); diff --git a/src/commands/client/query/index.test.ts b/src/commands/client/query/index.test.ts index cb019ea..03d25dd 100644 --- a/src/commands/client/query/index.test.ts +++ b/src/commands/client/query/index.test.ts @@ -1,6 +1,7 @@ import { afterAll, describe, expect, test } from "bun:test"; import { QueryCommand, UpsertCommand } from "@commands/index"; import { awaitUntilIndexed, newHttpClient, range, resetIndexes } from "@utils/test-utils"; +import { Index } from "@utils/test-utils"; const client = newHttpClient(); @@ -180,3 +181,108 @@ describe("QUERY", () => { { timeout: 20000 } ); }); + +describe("QUERY with Index Client", () => { + const index = new Index({ + token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + }); + + afterAll(async () => await resetIndexes()); + + test("should query records successfully", async () => { + const initialVector = range(0, 384); + const initialData = { id: 33, vector: initialVector }; + await index.upsert(initialData); + + await awaitUntilIndexed(index); + + const res = await index.query<{ hello: "World" }>({ + includeVectors: true, + vector: initialVector, + topK: 1, + }); + + expect(res).toEqual([ + { + id: "33", + score: 1, + vector: initialVector, + }, + ]); + }); + + test( + "should query with plain text successfully", + async () => { + index.upsert([ + { + id: "hello-world", + data: "testing-plan-text", + metadata: { upstash: "test" }, + }, + ]); + + await awaitUntilIndexed(index); + + const res = await index.query({ + data: "testing-plain-text", + topK: 1, + includeVectors: true, + includeMetadata: true, + }); + + expect(res[0].metadata).toEqual({ upstash: "test" }); + }, + { timeout: 20000 } + ); + + test("should narrow down the query results with filter", async () => { + const initialVector = range(0, 384); + const initialData = [ + { + id: 1, + vector: initialVector, + metadata: { + animal: "elephant", + tags: ["mammal"], + diet: "herbivore", + }, + }, + { + id: 2, + vector: initialVector, + metadata: { + animal: "tiger", + tags: ["mammal"], + diet: "carnivore", + }, + }, + ]; + + await index.upsert(initialData); + + await awaitUntilIndexed(index); + + const res = await index.query<{ + animal: string; + tags: string[]; + diet: string; + }>({ + vector: initialVector, + topK: 1, + filter: "tags[0] = 'mammal' AND diet = 'carnivore'", + includeVectors: true, + includeMetadata: true, + }); + + expect(res).toEqual([ + { + id: "2", + score: 1, + vector: initialVector, + metadata: { animal: "tiger", tags: ["mammal"], diet: "carnivore" }, + }, + ]); + }); +}); diff --git a/src/commands/client/range/index.test.ts b/src/commands/client/range/index.test.ts index 299ed10..584f0d9 100644 --- a/src/commands/client/range/index.test.ts +++ b/src/commands/client/range/index.test.ts @@ -1,6 +1,13 @@ import { afterAll, describe, expect, test } from "bun:test"; import { RangeCommand, UpsertCommand } from "@commands/index"; -import { newHttpClient, randomID, range, resetIndexes } from "@utils/test-utils"; +import { + Index, + awaitUntilIndexed, + newHttpClient, + randomID, + range, + resetIndexes, +} from "@utils/test-utils"; const client = newHttpClient(); @@ -15,6 +22,8 @@ describe("RANGE", () => { const payloads = randomizedData.map((data) => new UpsertCommand(data).exec(client)); await Promise.all(payloads); + await awaitUntilIndexed(client); + const res = await new RangeCommand({ cursor: 0, limit: 5, @@ -23,3 +32,29 @@ describe("RANGE", () => { expect(res.nextCursor).toBe("5"); }); }); + +describe("RANGE with Index Client", () => { + const index = new Index({ + token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + }); + + afterAll(async () => await resetIndexes()); + test("should query records successfully", async () => { + const randomizedData = new Array(20) + .fill("") + .map(() => ({ id: randomID(), vector: range(0, 384) })); + + await index.upsert(randomizedData); + + await awaitUntilIndexed(index); + + const res = await index.range({ + cursor: 0, + limit: 5, + includeVectors: true, + }); + + expect(res.nextCursor).toBe("5"); + }); +}); diff --git a/src/commands/client/update/index.test.ts b/src/commands/client/update/index.test.ts index 75d6c19..9edcab8 100644 --- a/src/commands/client/update/index.test.ts +++ b/src/commands/client/update/index.test.ts @@ -1,6 +1,6 @@ import { afterAll, describe, expect, test } from "bun:test"; import { FetchCommand, UpdateCommand, UpsertCommand } from "@commands/index"; -import { awaitUntilIndexed, newHttpClient, range, resetIndexes } from "@utils/test-utils"; +import { Index, awaitUntilIndexed, newHttpClient, range, resetIndexes } from "@utils/test-utils"; const client = newHttpClient(); @@ -31,3 +31,34 @@ describe("UPDATE", () => { expect(fetchData[0]?.metadata?.upstash).toBe("test-update"); }); }); + +describe("UPDATE with Index Client", () => { + const index = new Index({ + token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + }); + afterAll(async () => await resetIndexes()); + + test("should update vector metadata", async () => { + await index.upsert({ + id: 1, + vector: range(0, 384), + metadata: { upstash: "test-simple" }, + }); + + await awaitUntilIndexed(index); + + const res = await index.update({ + id: 1, + metadata: { upstash: "test-update" }, + }); + + expect(res).toEqual({ updated: 1 }); + + await awaitUntilIndexed(client, 5000); + + const fetchData = await index.fetch(["1"], { includeMetadata: true }); + + expect(fetchData[0]?.metadata?.upstash).toBe("test-update"); + }); +}); diff --git a/src/commands/client/upsert/index.test.ts b/src/commands/client/upsert/index.test.ts index 033c7b1..27eb084 100644 --- a/src/commands/client/upsert/index.test.ts +++ b/src/commands/client/upsert/index.test.ts @@ -1,15 +1,10 @@ import { afterAll, describe, expect, test } from "bun:test"; import { FetchCommand, UpsertCommand } from "@commands/index"; -import { newHttpClient, randomID, range, resetIndexes } from "@utils/test-utils"; -import { Index } from "../../../../index"; +import { Index, newHttpClient, randomID, range, resetIndexes } from "@utils/test-utils"; const client = newHttpClient(); describe("UPSERT", () => { - const index = new Index({ - url: process.env.UPSTASH_VECTOR_REST_URL!, - token: process.env.UPSTASH_VECTOR_REST_TOKEN!, - }); afterAll(async () => await resetIndexes()); test("should add record successfully", async () => { @@ -115,6 +110,38 @@ describe("UPSERT", () => { expect(resUpsert).toEqual("Success"); }); +}); + +describe("UPSERT with Index Client", () => { + const index = new Index({ + token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + }); + afterAll(async () => await resetIndexes()); + + test("should add record successfully", async () => { + const res = await index.upsert({ id: 1, vector: range(0, 384) }); + expect(res).toEqual("Success"); + }); + + // biome-ignore lint/nursery/useAwait: required to test bad payloads + test("should return an error when vector is missing", async () => { + const throwable = async () => { + //@ts-ignore + await new UpsertCommand({ id: 1 }).exec(client); + }; + expect(throwable).toThrow(); + }); + + test("should add data successfully with a metadata", async () => { + //@ts-ignore + const res = await new UpsertCommand({ + id: 1, + vector: range(0, 384), + metadata: { upstash: "test" }, + }).exec(client); + expect(res).toEqual("Success"); + }); test("should run with index.upsert in bulk", async () => { const upsertData = [ @@ -133,4 +160,36 @@ describe("UPSERT", () => { expect(resUpsert).toEqual("Success"); }); + + test("should add plain text as data successfully", async () => { + const res = await index.upsert([ + { + id: "hello-world", + data: "Test1-2-3-4-5", + metadata: { upstash: "test" }, + }, + ]); + expect(res).toEqual("Success"); + }); + + test("should fail to upsert due to mixed usage of vector and plain text", () => { + const throwable = async () => { + await index.upsert([ + { + id: "hello-world", + + data: "Test1-2-3-4-5", + metadata: { upstash: "test" }, + }, + { + id: "hello-world", + //@ts-expect-error Mixed usage of vector and data in the same upsert command is not allowed. + vector: [1, 2, 3, 4], + metadata: { upstash: "test" }, + }, + ]); + }; + + expect(throwable).toThrow(); + }); }); diff --git a/src/utils/test-utils.ts b/src/utils/test-utils.ts index 3562ee0..673d902 100644 --- a/src/utils/test-utils.ts +++ b/src/utils/test-utils.ts @@ -3,6 +3,7 @@ import { InfoCommand } from "../commands/client/info"; import { ResetCommand } from "../commands/client/reset"; import { HttpClient, RetryConfig } from "../http"; import { Index } from "../vector"; +export * from "../../index"; export type NonArrayType = T extends Array ? U : T; From 39a2d2ce00f0de9e043c49e358f0a40d5650eafd Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Wed, 29 May 2024 14:42:44 +0200 Subject: [PATCH 02/14] fix: target indexes --- src/commands/client/query/index.test.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/commands/client/query/index.test.ts b/src/commands/client/query/index.test.ts index 03d25dd..b8dccfa 100644 --- a/src/commands/client/query/index.test.ts +++ b/src/commands/client/query/index.test.ts @@ -1,6 +1,6 @@ import { afterAll, describe, expect, test } from "bun:test"; import { QueryCommand, UpsertCommand } from "@commands/index"; -import { awaitUntilIndexed, newHttpClient, range, resetIndexes } from "@utils/test-utils"; +import { awaitUntilIndexed, newHttpClient, randomID, range, resetIndexes } from "@utils/test-utils"; import { Index } from "@utils/test-utils"; const client = newHttpClient(); @@ -184,6 +184,11 @@ describe("QUERY", () => { describe("QUERY with Index Client", () => { const index = new Index({ + token: process.env.UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.UPSTASH_VECTOR_REST_URL!, + }); + + const embeddingIndex = new Index({ token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, }); @@ -191,8 +196,9 @@ describe("QUERY with Index Client", () => { afterAll(async () => await resetIndexes()); test("should query records successfully", async () => { + const ID = randomID(); const initialVector = range(0, 384); - const initialData = { id: 33, vector: initialVector }; + const initialData = { id: ID, vector: initialVector }; await index.upsert(initialData); await awaitUntilIndexed(index); @@ -205,7 +211,7 @@ describe("QUERY with Index Client", () => { expect(res).toEqual([ { - id: "33", + id: ID, score: 1, vector: initialVector, }, @@ -215,7 +221,7 @@ describe("QUERY with Index Client", () => { test( "should query with plain text successfully", async () => { - index.upsert([ + embeddingIndex.upsert([ { id: "hello-world", data: "testing-plan-text", @@ -223,9 +229,9 @@ describe("QUERY with Index Client", () => { }, ]); - await awaitUntilIndexed(index); + await awaitUntilIndexed(embeddingIndex); - const res = await index.query({ + const res = await embeddingIndex.query({ data: "testing-plain-text", topK: 1, includeVectors: true, @@ -238,10 +244,11 @@ describe("QUERY with Index Client", () => { ); test("should narrow down the query results with filter", async () => { + const ID = randomID(); const initialVector = range(0, 384); const initialData = [ { - id: 1, + id: `1-${ID}`, vector: initialVector, metadata: { animal: "elephant", @@ -250,7 +257,7 @@ describe("QUERY with Index Client", () => { }, }, { - id: 2, + id: `2-${ID}`, vector: initialVector, metadata: { animal: "tiger", @@ -278,7 +285,7 @@ describe("QUERY with Index Client", () => { expect(res).toEqual([ { - id: "2", + id: `2-${ID}`, score: 1, vector: initialVector, metadata: { animal: "tiger", tags: ["mammal"], diet: "carnivore" }, From 11d504c14e51ec3613464ba24f0831de9e26ffb0 Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Wed, 29 May 2024 14:45:29 +0200 Subject: [PATCH 03/14] update range func with random numbers --- src/utils/test-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/test-utils.ts b/src/utils/test-utils.ts index 673d902..2ed1200 100644 --- a/src/utils/test-utils.ts +++ b/src/utils/test-utils.ts @@ -58,7 +58,7 @@ export const resetIndexes = async () => await new ResetCommand().exec(newHttpCli export const range = (start: number, end: number, step = 1) => { const result = []; for (let i = start; i < end; i += step) { - result.push(i); + result.push(Math.random()); } return result; }; From 353ad7e0d2480773d540d4bb074576cfb4c95e71 Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Wed, 29 May 2024 14:51:21 +0200 Subject: [PATCH 04/14] deparallelize tests --- src/commands/client/query/index.test.ts | 182 ++++++++++++------------ src/utils/test-utils.ts | 2 +- 2 files changed, 93 insertions(+), 91 deletions(-) diff --git a/src/commands/client/query/index.test.ts b/src/commands/client/query/index.test.ts index b8dccfa..0824d0a 100644 --- a/src/commands/client/query/index.test.ts +++ b/src/commands/client/query/index.test.ts @@ -6,6 +6,15 @@ import { Index } from "@utils/test-utils"; const client = newHttpClient(); describe("QUERY", () => { + const index = new Index({ + token: process.env.UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.UPSTASH_VECTOR_REST_URL!, + }); + + const embeddingIndex = new Index({ + token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + }); afterAll(async () => await resetIndexes()); test("should query records successfully", async () => { const initialVector = range(0, 384); @@ -180,116 +189,109 @@ describe("QUERY", () => { }, { timeout: 20000 } ); -}); -describe("QUERY with Index Client", () => { - const index = new Index({ - token: process.env.UPSTASH_VECTOR_REST_TOKEN!, - url: process.env.UPSTASH_VECTOR_REST_URL!, - }); + describe("with Index Client", () => { - const embeddingIndex = new Index({ - token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, - url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, - }); + afterAll(async () => await resetIndexes()); - afterAll(async () => await resetIndexes()); + test("should query records successfully", async () => { + const ID = randomID(); + const initialVector = range(0, 384); + const initialData = { id: ID, vector: initialVector }; + await index.upsert(initialData); - test("should query records successfully", async () => { - const ID = randomID(); - const initialVector = range(0, 384); - const initialData = { id: ID, vector: initialVector }; - await index.upsert(initialData); + await awaitUntilIndexed(index); - await awaitUntilIndexed(index); + const res = await index.query<{ hello: "World" }>({ + includeVectors: true, + vector: initialVector, + topK: 1, + }); - const res = await index.query<{ hello: "World" }>({ - includeVectors: true, - vector: initialVector, - topK: 1, + expect(res).toEqual([ + { + id: ID, + score: 1, + vector: initialVector, + }, + ]); }); - expect(res).toEqual([ - { - id: ID, - score: 1, - vector: initialVector, + test( + "should query with plain text successfully", + async () => { + embeddingIndex.upsert([ + { + id: "hello-world", + data: "testing-plan-text", + metadata: { upstash: "test" }, + }, + ]); + + await awaitUntilIndexed(embeddingIndex); + + const res = await embeddingIndex.query({ + data: "testing-plain-text", + topK: 1, + includeVectors: true, + includeMetadata: true, + }); + + expect(res[0].metadata).toEqual({ upstash: "test" }); }, - ]); - }); + { timeout: 20000 } + ); - test( - "should query with plain text successfully", - async () => { - embeddingIndex.upsert([ + test("should narrow down the query results with filter", async () => { + const ID = randomID(); + const initialVector = range(0, 384); + const initialData = [ { - id: "hello-world", - data: "testing-plan-text", - metadata: { upstash: "test" }, + id: `1-${ID}`, + vector: initialVector, + metadata: { + animal: "elephant", + tags: ["mammal"], + diet: "herbivore", + }, }, - ]); + { + id: `2-${ID}`, + vector: initialVector, + metadata: { + animal: "tiger", + tags: ["mammal"], + diet: "carnivore", + }, + }, + ]; - await awaitUntilIndexed(embeddingIndex); + await index.upsert(initialData); - const res = await embeddingIndex.query({ - data: "testing-plain-text", + await awaitUntilIndexed(index); + + const res = await index.query<{ + animal: string; + tags: string[]; + diet: string; + }>({ + vector: initialVector, topK: 1, + filter: "tags[0] = 'mammal' AND diet = 'carnivore'", includeVectors: true, includeMetadata: true, }); - expect(res[0].metadata).toEqual({ upstash: "test" }); - }, - { timeout: 20000 } - ); - - test("should narrow down the query results with filter", async () => { - const ID = randomID(); - const initialVector = range(0, 384); - const initialData = [ - { - id: `1-${ID}`, - vector: initialVector, - metadata: { - animal: "elephant", - tags: ["mammal"], - diet: "herbivore", - }, - }, - { - id: `2-${ID}`, - vector: initialVector, - metadata: { - animal: "tiger", - tags: ["mammal"], - diet: "carnivore", + expect(res).toEqual([ + { + id: `2-${ID}`, + score: 1, + vector: initialVector, + metadata: { animal: "tiger", tags: ["mammal"], diet: "carnivore" }, }, - }, - ]; - - await index.upsert(initialData); - - await awaitUntilIndexed(index); - - const res = await index.query<{ - animal: string; - tags: string[]; - diet: string; - }>({ - vector: initialVector, - topK: 1, - filter: "tags[0] = 'mammal' AND diet = 'carnivore'", - includeVectors: true, - includeMetadata: true, + ]); }); - - expect(res).toEqual([ - { - id: `2-${ID}`, - score: 1, - vector: initialVector, - metadata: { animal: "tiger", tags: ["mammal"], diet: "carnivore" }, - }, - ]); }); }); + + diff --git a/src/utils/test-utils.ts b/src/utils/test-utils.ts index 2ed1200..673d902 100644 --- a/src/utils/test-utils.ts +++ b/src/utils/test-utils.ts @@ -58,7 +58,7 @@ export const resetIndexes = async () => await new ResetCommand().exec(newHttpCli export const range = (start: number, end: number, step = 1) => { const result = []; for (let i = start; i < end; i += step) { - result.push(Math.random()); + result.push(i); } return result; }; From 003d79309ae21e1c2dd50675a0e92d06d1e5e921 Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Wed, 29 May 2024 14:51:45 +0200 Subject: [PATCH 05/14] fmt --- src/commands/client/query/index.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/commands/client/query/index.test.ts b/src/commands/client/query/index.test.ts index 0824d0a..e5cb0dd 100644 --- a/src/commands/client/query/index.test.ts +++ b/src/commands/client/query/index.test.ts @@ -191,7 +191,6 @@ describe("QUERY", () => { ); describe("with Index Client", () => { - afterAll(async () => await resetIndexes()); test("should query records successfully", async () => { @@ -293,5 +292,3 @@ describe("QUERY", () => { }); }); }); - - From f34a3eb7a399868a1dfb46d67abbdf26eeaddc8b Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Wed, 29 May 2024 14:56:29 +0200 Subject: [PATCH 06/14] trigger --- src/commands/client/reset/index.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands/client/reset/index.test.ts b/src/commands/client/reset/index.test.ts index b4f7fee..469a7f1 100644 --- a/src/commands/client/reset/index.test.ts +++ b/src/commands/client/reset/index.test.ts @@ -23,6 +23,7 @@ describe("RESET", () => { expect(res).toEqual(randomizedData); + await new ResetCommand().exec(client); const resAfterReset = await new FetchCommand([ randomizedData.map((x) => x.id), From 020bb2936c4fd0783b011fcb528663a64d3811bd Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Wed, 29 May 2024 15:05:11 +0200 Subject: [PATCH 07/14] fix target and reset after all --- src/commands/client/delete/index.test.ts | 7 +++++-- src/commands/client/fetch/index.test.ts | 4 ++-- src/commands/client/query/index.test.ts | 4 ++++ src/commands/client/range/index.test.ts | 8 +++++--- src/commands/client/reset/index.test.ts | 1 - src/commands/client/update/index.test.ts | 8 +++++--- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/commands/client/delete/index.test.ts b/src/commands/client/delete/index.test.ts index 855a22e..71e7fb4 100644 --- a/src/commands/client/delete/index.test.ts +++ b/src/commands/client/delete/index.test.ts @@ -51,8 +51,11 @@ describe("DELETE", () => { describe("DELETE with Index Client", () => { const index = new Index({ - token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, - url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + token: process.env.UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.UPSTASH_VECTOR_REST_URL!, + }); + afterAll(async () => { + await index.reset(); }); test("should delete single record succesfully", () => { diff --git a/src/commands/client/fetch/index.test.ts b/src/commands/client/fetch/index.test.ts index bae5448..75544fc 100644 --- a/src/commands/client/fetch/index.test.ts +++ b/src/commands/client/fetch/index.test.ts @@ -59,8 +59,8 @@ describe("FETCH", () => { describe("FETCH with Index Client", () => { const index = new Index({ - token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, - url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + token: process.env.UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.UPSTASH_VECTOR_REST_URL!, }); test("should fetch array of records by IDs succesfully", async () => { diff --git a/src/commands/client/query/index.test.ts b/src/commands/client/query/index.test.ts index e5cb0dd..3cd84b0 100644 --- a/src/commands/client/query/index.test.ts +++ b/src/commands/client/query/index.test.ts @@ -16,6 +16,10 @@ describe("QUERY", () => { url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, }); afterAll(async () => await resetIndexes()); + afterAll(async () => { + await index.reset(); + await embeddingIndex.reset(); + }); test("should query records successfully", async () => { const initialVector = range(0, 384); const initialData = { id: 33, vector: initialVector }; diff --git a/src/commands/client/range/index.test.ts b/src/commands/client/range/index.test.ts index 584f0d9..717df93 100644 --- a/src/commands/client/range/index.test.ts +++ b/src/commands/client/range/index.test.ts @@ -35,11 +35,13 @@ describe("RANGE", () => { describe("RANGE with Index Client", () => { const index = new Index({ - token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, - url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + token: process.env.UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.UPSTASH_VECTOR_REST_URL!, }); - afterAll(async () => await resetIndexes()); + afterAll(async () => { + await index.reset(); + }); test("should query records successfully", async () => { const randomizedData = new Array(20) .fill("") diff --git a/src/commands/client/reset/index.test.ts b/src/commands/client/reset/index.test.ts index 469a7f1..b4f7fee 100644 --- a/src/commands/client/reset/index.test.ts +++ b/src/commands/client/reset/index.test.ts @@ -23,7 +23,6 @@ describe("RESET", () => { expect(res).toEqual(randomizedData); - await new ResetCommand().exec(client); const resAfterReset = await new FetchCommand([ randomizedData.map((x) => x.id), diff --git a/src/commands/client/update/index.test.ts b/src/commands/client/update/index.test.ts index 9edcab8..7907c94 100644 --- a/src/commands/client/update/index.test.ts +++ b/src/commands/client/update/index.test.ts @@ -34,10 +34,12 @@ describe("UPDATE", () => { describe("UPDATE with Index Client", () => { const index = new Index({ - token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, - url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + token: process.env.UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.UPSTASH_VECTOR_REST_URL!, + }); + afterAll(async () => { + await index.reset(); }); - afterAll(async () => await resetIndexes()); test("should update vector metadata", async () => { await index.upsert({ From 58238c4344847b69cad8299f37ed5fbee58d9359 Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Sat, 1 Jun 2024 01:37:00 +0200 Subject: [PATCH 08/14] update: query tests nesting --- src/commands/client/query/index.test.ts | 173 +++++++++++++----------- 1 file changed, 92 insertions(+), 81 deletions(-) diff --git a/src/commands/client/query/index.test.ts b/src/commands/client/query/index.test.ts index 3cd84b0..64b9ac3 100644 --- a/src/commands/client/query/index.test.ts +++ b/src/commands/client/query/index.test.ts @@ -194,105 +194,116 @@ describe("QUERY", () => { { timeout: 20000 } ); - describe("with Index Client", () => { - afterAll(async () => await resetIndexes()); - test("should query records successfully", async () => { - const ID = randomID(); - const initialVector = range(0, 384); - const initialData = { id: ID, vector: initialVector }; - await index.upsert(initialData); +}); - await awaitUntilIndexed(index); +describe("with Index Client", () => { + const index = new Index({ + token: process.env.UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.UPSTASH_VECTOR_REST_URL!, + }); + const embeddingIndex = new Index({ + token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, + url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, + }); + afterAll(async () => await resetIndexes()); - const res = await index.query<{ hello: "World" }>({ - includeVectors: true, - vector: initialVector, - topK: 1, - }); + test("should query records successfully", async () => { + const ID = randomID(); + const initialVector = range(0, 384); + const initialData = { id: ID, vector: initialVector }; + await index.upsert(initialData); - expect(res).toEqual([ - { - id: ID, - score: 1, - vector: initialVector, - }, - ]); + await awaitUntilIndexed(index); + + const res = await index.query<{ hello: "World" }>({ + includeVectors: true, + vector: initialVector, + topK: 1, }); - test( - "should query with plain text successfully", - async () => { - embeddingIndex.upsert([ - { - id: "hello-world", - data: "testing-plan-text", - metadata: { upstash: "test" }, - }, - ]); - - await awaitUntilIndexed(embeddingIndex); - - const res = await embeddingIndex.query({ - data: "testing-plain-text", - topK: 1, - includeVectors: true, - includeMetadata: true, - }); - - expect(res[0].metadata).toEqual({ upstash: "test" }); + + expect(res).toEqual([ + { + id: ID, + score: 1, + vector: initialVector, }, - { timeout: 20000 } - ); + ]); + }); - test("should narrow down the query results with filter", async () => { - const ID = randomID(); - const initialVector = range(0, 384); - const initialData = [ - { - id: `1-${ID}`, - vector: initialVector, - metadata: { - animal: "elephant", - tags: ["mammal"], - diet: "herbivore", - }, - }, + test( + "should query with plain text successfully", + async () => { + embeddingIndex.upsert([ { - id: `2-${ID}`, - vector: initialVector, - metadata: { - animal: "tiger", - tags: ["mammal"], - diet: "carnivore", - }, + id: "hello-world", + data: "testing-plan-text", + metadata: { upstash: "test" }, }, - ]; - - await index.upsert(initialData); + ]); - await awaitUntilIndexed(index); + await awaitUntilIndexed(embeddingIndex); - const res = await index.query<{ - animal: string; - tags: string[]; - diet: string; - }>({ - vector: initialVector, + const res = await embeddingIndex.query({ + data: "testing-plain-text", topK: 1, - filter: "tags[0] = 'mammal' AND diet = 'carnivore'", includeVectors: true, includeMetadata: true, }); - expect(res).toEqual([ - { - id: `2-${ID}`, - score: 1, - vector: initialVector, - metadata: { animal: "tiger", tags: ["mammal"], diet: "carnivore" }, + expect(res[0].metadata).toEqual({ upstash: "test" }); + }, + { timeout: 20000 } + ); + + test("should narrow down the query results with filter", async () => { + const ID = randomID(); + const initialVector = range(0, 384); + const initialData = [ + { + id: `1-${ID}`, + vector: initialVector, + metadata: { + animal: "elephant", + tags: ["mammal"], + diet: "herbivore", }, - ]); + }, + { + id: `2-${ID}`, + vector: initialVector, + metadata: { + animal: "tiger", + tags: ["mammal"], + diet: "carnivore", + }, + }, + ]; + + await index.upsert(initialData); + + await awaitUntilIndexed(index); + + const res = await index.query<{ + animal: string; + tags: string[]; + diet: string; + }>({ + vector: initialVector, + topK: 1, + filter: "tags[0] = 'mammal' AND diet = 'carnivore'", + includeVectors: true, + includeMetadata: true, }); + + expect(res).toEqual([ + { + id: `2-${ID}`, + score: 1, + vector: initialVector, + metadata: { animal: "tiger", tags: ["mammal"], diet: "carnivore" }, + }, + ]); }); }); From f0bdcdaaa1df5b76f86b39f2c2085a9e1c7221ba Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Sat, 1 Jun 2024 01:38:02 +0200 Subject: [PATCH 09/14] fmt --- src/commands/client/query/index.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/commands/client/query/index.test.ts b/src/commands/client/query/index.test.ts index 64b9ac3..aca9170 100644 --- a/src/commands/client/query/index.test.ts +++ b/src/commands/client/query/index.test.ts @@ -193,8 +193,6 @@ describe("QUERY", () => { }, { timeout: 20000 } ); - - }); describe("with Index Client", () => { @@ -222,7 +220,6 @@ describe("with Index Client", () => { topK: 1, }); - expect(res).toEqual([ { id: ID, From 2aab20566ad2c9f72fe4e6163b4d79210a9f1aae Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Sat, 1 Jun 2024 01:42:04 +0200 Subject: [PATCH 10/14] randomize mock vector generation --- src/utils/test-utils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils/test-utils.ts b/src/utils/test-utils.ts index 673d902..c1c491a 100644 --- a/src/utils/test-utils.ts +++ b/src/utils/test-utils.ts @@ -58,11 +58,14 @@ export const resetIndexes = async () => await new ResetCommand().exec(newHttpCli export const range = (start: number, end: number, step = 1) => { const result = []; for (let i = start; i < end; i += step) { - result.push(i); + const randomNum = Math.floor(Math.random() * (end - start + 1)) + start; + result.push(randomNum); } return result; }; + + export const awaitUntilIndexed = async (client: HttpClient | Index, timeoutMillis = 10_000) => { const start = performance.now(); From 07349442049993da9e4a875a85a86a3336056808 Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Sat, 1 Jun 2024 01:47:35 +0200 Subject: [PATCH 11/14] improve vector cleanup --- src/commands/client/query/index.test.ts | 14 +++++++++----- src/utils/test-utils.ts | 2 -- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/commands/client/query/index.test.ts b/src/commands/client/query/index.test.ts index aca9170..a06ff4e 100644 --- a/src/commands/client/query/index.test.ts +++ b/src/commands/client/query/index.test.ts @@ -1,6 +1,6 @@ import { afterAll, describe, expect, test } from "bun:test"; import { QueryCommand, UpsertCommand } from "@commands/index"; -import { awaitUntilIndexed, newHttpClient, randomID, range, resetIndexes } from "@utils/test-utils"; +import { awaitUntilIndexed, newHttpClient, randomID, range } from "@utils/test-utils"; import { Index } from "@utils/test-utils"; const client = newHttpClient(); @@ -15,7 +15,7 @@ describe("QUERY", () => { token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, }); - afterAll(async () => await resetIndexes()); + afterAll(async () => { await index.reset(); await embeddingIndex.reset(); @@ -204,7 +204,11 @@ describe("with Index Client", () => { token: process.env.EMBEDDING_UPSTASH_VECTOR_REST_TOKEN!, url: process.env.EMBEDDING_UPSTASH_VECTOR_REST_URL!, }); - afterAll(async () => await resetIndexes()); + + afterAll(async () => { + await index.reset(); + await embeddingIndex.reset(); + }); test("should query records successfully", async () => { const ID = randomID(); @@ -235,7 +239,7 @@ describe("with Index Client", () => { embeddingIndex.upsert([ { id: "hello-world", - data: "testing-plan-text", + data: "with-index-plain-text-query-test", metadata: { upstash: "test" }, }, ]); @@ -243,7 +247,7 @@ describe("with Index Client", () => { await awaitUntilIndexed(embeddingIndex); const res = await embeddingIndex.query({ - data: "testing-plain-text", + data: "with-index-plain-text-query-test", topK: 1, includeVectors: true, includeMetadata: true, diff --git a/src/utils/test-utils.ts b/src/utils/test-utils.ts index c1c491a..73f7ab1 100644 --- a/src/utils/test-utils.ts +++ b/src/utils/test-utils.ts @@ -64,8 +64,6 @@ export const range = (start: number, end: number, step = 1) => { return result; }; - - export const awaitUntilIndexed = async (client: HttpClient | Index, timeoutMillis = 10_000) => { const start = performance.now(); From 7324b68fc952cb0b69d623329c9a789d7dfac160 Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Sat, 1 Jun 2024 01:51:09 +0200 Subject: [PATCH 12/14] fix: await upsert --- src/commands/client/query/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/client/query/index.test.ts b/src/commands/client/query/index.test.ts index a06ff4e..eb56625 100644 --- a/src/commands/client/query/index.test.ts +++ b/src/commands/client/query/index.test.ts @@ -236,7 +236,7 @@ describe("with Index Client", () => { test( "should query with plain text successfully", async () => { - embeddingIndex.upsert([ + await embeddingIndex.upsert([ { id: "hello-world", data: "with-index-plain-text-query-test", From 83c1ff62590a32b0a6700fac886025c0249cfd86 Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Sat, 1 Jun 2024 01:52:58 +0200 Subject: [PATCH 13/14] fix: range test explanations --- src/commands/client/range/index.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/client/range/index.test.ts b/src/commands/client/range/index.test.ts index 717df93..8786e3d 100644 --- a/src/commands/client/range/index.test.ts +++ b/src/commands/client/range/index.test.ts @@ -14,7 +14,7 @@ const client = newHttpClient(); describe("RANGE", () => { afterAll(async () => await resetIndexes()); - test("should query records successfully", async () => { + test("should paginate records successfully", async () => { const randomizedData = new Array(20) .fill("") .map(() => ({ id: randomID(), vector: range(0, 384) })); @@ -42,7 +42,7 @@ describe("RANGE with Index Client", () => { afterAll(async () => { await index.reset(); }); - test("should query records successfully", async () => { + test("should paginate records successfully", async () => { const randomizedData = new Array(20) .fill("") .map(() => ({ id: randomID(), vector: range(0, 384) })); From 4eb2336d624db35ebca9b698df8258dd18cbaa28 Mon Sep 17 00:00:00 2001 From: fahreddinozcan Date: Mon, 3 Jun 2024 09:58:34 +0200 Subject: [PATCH 14/14] update delete res check to include explicit object --- src/commands/client/delete/index.test.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/commands/client/delete/index.test.ts b/src/commands/client/delete/index.test.ts index 71e7fb4..53d7c63 100644 --- a/src/commands/client/delete/index.test.ts +++ b/src/commands/client/delete/index.test.ts @@ -58,14 +58,17 @@ describe("DELETE with Index Client", () => { await index.reset(); }); - test("should delete single record succesfully", () => { + test("should delete single record succesfully", async () => { const initialVector = range(0, 384); const id = randomID(); index.upsert({ id, vector: initialVector }); - const deletionResult = index.delete(id); - expect(deletionResult).toBeTruthy(); + const deletionResult = await index.delete(id); + + expect(deletionResult).toEqual({ + deleted: 1 + }); }); test("should delete array of records successfully", async () => { @@ -77,6 +80,8 @@ describe("DELETE with Index Client", () => { await Promise.all(upsertPromises); const deletionResult = await index.delete(idsToUpsert); - expect(deletionResult).toBeTruthy(); + expect(deletionResult).toEqual({ + deleted: 3, + }) }); });