From c12b4e3334ca80e58acd677ad28f1f61fc3cb10d Mon Sep 17 00:00:00 2001 From: Lindy Xu Date: Tue, 18 Aug 2026 04:49:44 +0800 Subject: [PATCH] fix: preserve vector-first ANN query plan --- src/services/turso/vector-search.ts | 4 ++-- tests/turso-vector-search.test.ts | 32 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/services/turso/vector-search.ts b/src/services/turso/vector-search.ts index b562caf..8e975e8 100644 --- a/src/services/turso/vector-search.ts +++ b/src/services/turso/vector-search.ts @@ -236,13 +236,13 @@ export class TursoVectorSearch { ? ` SELECT m.id AS id, vector_distance_cos(m.${columnName}, vector32(?)) AS dist FROM vector_top_k('${indexName}', vector32(?), ?) AS v - JOIN memories m ON m.rowid = v.id + CROSS JOIN memories m ON m.rowid = v.id WHERE m.${columnName} IS NOT NULL ` : ` SELECT m.id AS id, vector_distance_cos(m.${columnName}, vector32(?)) AS dist FROM vector_top_k('${indexName}', vector32(?), ?) AS v - JOIN memories m ON m.rowid = v.id + CROSS JOIN memories m ON m.rowid = v.id WHERE m.${columnName} IS NOT NULL AND m.container_tag = ? `, containerTag === "" ? [queryJson, queryJson, k] : [queryJson, queryJson, k, containerTag] diff --git a/tests/turso-vector-search.test.ts b/tests/turso-vector-search.test.ts index 3534086..bbcda1c 100644 --- a/tests/turso-vector-search.test.ts +++ b/tests/turso-vector-search.test.ts @@ -68,4 +68,36 @@ describe("turso vector search", () => { ); expect(limitedResults).toHaveLength(1); }); + + it("keeps vector_top_k before memories for filtered and unfiltered ANN queries", async () => { + const observedSql: string[] = []; + const db = { + all: async (sql: string) => { + observedSql.push(sql); + return []; + }, + }; + const { tursoVectorSearch } = await import("../src/services/turso/vector-search.js"); + const search = tursoVectorSearch as unknown as { + searchKind( + database: typeof db, + queryJson: string, + k: number, + containerTag: string, + indexName: string, + columnName: string + ): Promise>; + }; + + await search.searchKind(db, "[1,0]", 10, "", "memories_vec_idx", "vector"); + await search.searchKind(db, "[1,0]", 10, "opencode_project_test", "memories_vec_idx", "vector"); + + expect(observedSql).toHaveLength(2); + for (const sql of observedSql) { + expect(sql).toContain("FROM vector_top_k"); + expect(sql).toContain("CROSS JOIN memories m ON m.rowid = v.id"); + expect(sql.indexOf("vector_top_k")).toBeLessThan(sql.indexOf("memories m")); + } + expect(observedSql[1]).toContain("m.container_tag = ?"); + }); });