From f211647b8efe85a9609e7a6bdf0a6be82e7aef9e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 13:55:27 +0000 Subject: [PATCH] perf(dashboard): scope lesson progress queries Optimize db queries in dashboard and courses pages to only fetch progress for lessons belonging to published courses. Co-authored-by: projectamazonph <286085559+projectamazonph@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ src/app/(dashboard)/courses/page.tsx | 16 +++++++++++----- src/app/(dashboard)/dashboard/page.tsx | 17 +++++++++++------ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index ba67a71..71b5eaf 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -3,3 +3,7 @@ ## 2026-07-16 - [O(N*M) Nested Loop Lookups in Grading Engines] **Learning:** In interactive scenarios (such as Bid Elevator and STR Triage), grading engines frequently iterate over user decisions and match them against scenario properties (like keywords or search terms). Performing `array.find()` inside loop bodies or filter predicates results in costly O(N*M) lookups. **Action:** Convert arrays to `Map` lookups before entering loops/nested scans. Mapping keys once in O(M) time enables O(1) lookups during execution, transforming the time complexity of the grading logic to O(N + M). + +## 2026-07-30 - [Scoping User Lesson Progress Queries] +**Learning:** Querying user-specific lesson progress history without a courses or lessons filter fetches the user's entire completion history. As the student completes more lessons, this payload grows unboundedly, increasing database round-trip latency, payload size, and server-side memory consumption. +**Action:** Always extract the relevant lesson IDs first from the courses currently being queried or rendered, and explicitly scope the progress lookup with `lessonId: { in: allLessonIds }`. diff --git a/src/app/(dashboard)/courses/page.tsx b/src/app/(dashboard)/courses/page.tsx index 1d0c6c3..ed67e8b 100644 --- a/src/app/(dashboard)/courses/page.tsx +++ b/src/app/(dashboard)/courses/page.tsx @@ -37,11 +37,17 @@ export default async function CoursesIndexPage() { }, }); - // Get user's lesson progress - const lessonProgress = await db.lessonProgress.findMany({ - where: { userId: user.id, deletedAt: null }, - select: { lessonId: true, status: true }, - }); + const allLessons = courses.flatMap((c) => c.modules.flatMap((m) => m.lessons)); + const allLessonIds = allLessons.map((l) => l.id); + + // Bolt optimization: Scope lessonProgress query to only the relevant lesson IDs + // to avoid fetching the user's entire history and reduce database/memory footprint. + const lessonProgress = allLessonIds.length > 0 + ? await db.lessonProgress.findMany({ + where: { userId: user.id, lessonId: { in: allLessonIds }, deletedAt: null }, + select: { lessonId: true, status: true }, + }) + : []; const progressMap = new Map(lessonProgress.map((p) => [p.lessonId, p.status])); return ( diff --git a/src/app/(dashboard)/dashboard/page.tsx b/src/app/(dashboard)/dashboard/page.tsx index 546298d..ef51d88 100644 --- a/src/app/(dashboard)/dashboard/page.tsx +++ b/src/app/(dashboard)/dashboard/page.tsx @@ -30,15 +30,20 @@ export default async function DashboardPage() { }, }); - // Get user's lesson progress - const lessonProgress = await db.lessonProgress.findMany({ - where: { userId: user.id, deletedAt: null }, - select: { lessonId: true, status: true }, - }); + const allLessons = courses.flatMap((c) => c.modules.flatMap((m) => m.lessons)); + const allLessonIds = allLessons.map((l) => l.id); + + // Bolt optimization: Scope lessonProgress query to only the relevant lesson IDs + // to avoid fetching the user's entire history and reduce database/memory footprint. + const lessonProgress = allLessonIds.length > 0 + ? await db.lessonProgress.findMany({ + where: { userId: user.id, lessonId: { in: allLessonIds }, deletedAt: null }, + select: { lessonId: true, status: true }, + }) + : []; const progressMap = new Map(lessonProgress.map((p) => [p.lessonId, p.status])); // Compute aggregate stats - const allLessons = courses.flatMap((c) => c.modules.flatMap((m) => m.lessons)); const totalLessons = allLessons.length; const completedLessons = allLessons.filter((l) => progressMap.get(l.id) === ProgressStatus.COMPLETED).length; const inProgressLessons = allLessons.filter((l) => progressMap.get(l.id) === ProgressStatus.IN_PROGRESS).length;