From 29fb68acc3043d13f0f0932c2560b68d493e8cba Mon Sep 17 00:00:00 2001 From: lhuigou Date: Sun, 5 Apr 2026 15:22:50 +0800 Subject: [PATCH] fix: use indexed query for soul list to avoid over-fetching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The souls.list query previously used take(limit * 5) followed by JS filtering on softDeletedAt, reading up to 5× more documents than needed. - For the ownerUserId branch: use Convex .filter() on the by_owner index to skip deleted rows before truncating. - For the browse branch: use the by_active_updated compound index with eq(softDeletedAt, undefined) to skip deleted rows at the index level. This reduces document reads and bandwidth, especially as the souls table grows. --- convex/souls.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/convex/souls.ts b/convex/souls.ts index c72867223..dc604f4a3 100644 --- a/convex/souls.ts +++ b/convex/souls.ts @@ -125,24 +125,27 @@ export const list = query({ const limit = args.limit ?? 24; const ownerUserId = args.ownerUserId; if (ownerUserId) { + // No compound index on (ownerUserId, softDeletedAt) exists, so use + // the by_owner index and let Convex filter out deleted rows before + // truncating. This avoids the previous take(limit*5) over-fetch. const entries = await ctx.db .query("souls") .withIndex("by_owner", (q) => q.eq("ownerUserId", ownerUserId)) .order("desc") - .take(limit * 5); + .filter((q) => q.eq(q.field("softDeletedAt"), undefined)) + .take(limit); return entries - .filter((soul) => !soul.softDeletedAt) - .slice(0, limit) .map((soul) => toPublicSoul(soul)) .filter((soul): soul is NonNullable => Boolean(soul)); } + // Use the by_active_updated index to skip soft-deleted rows at the + // index level instead of reading 5× documents and filtering in JS. const entries = await ctx.db .query("souls") + .withIndex("by_active_updated", (q) => q.eq("softDeletedAt", undefined)) .order("desc") - .take(limit * 5); + .take(limit); return entries - .filter((soul) => !soul.softDeletedAt) - .slice(0, limit) .map((soul) => toPublicSoul(soul)) .filter((soul): soul is NonNullable => Boolean(soul)); },