diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..8843477 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-06 - Fixing N+1 Queries in User Orders +**Learning:** In Drizzle ORM, querying related items within a loop (e.g., fetching order items for each order inside `Promise.all(orders.map(...))`) creates a classic N+1 query bottleneck. This codebase's serverless Neon PostgreSQL setup makes query round-trip times especially punitive. +**Action:** Always batch related fetches. Use `inArray` to query all related items in a single shot (e.g., `where(inArray(orderItems.orderId, orderIds))`) and then map them in memory using a hash map or `Map` structure for O(1) assignment. diff --git a/lib/actions/orders.ts b/lib/actions/orders.ts index 3d367bb..26dfd40 100644 --- a/lib/actions/orders.ts +++ b/lib/actions/orders.ts @@ -3,7 +3,7 @@ import { db } from "@/lib/db"; import { orders, orderItems, products, productVariants, user, bargainSessions } from "@/lib/db/schema"; import { getServerSession } from "@/lib/auth-server"; -import { eq, desc, sql, and, isNull } from "drizzle-orm"; +import { eq, desc, sql, and, isNull, inArray } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { markCouponUsed } from "./admin"; @@ -297,20 +297,31 @@ export async function getUserOrders() { .where(eq(orders.userId, session.user.id)) .orderBy(desc(orders.createdAt)); - // Fetch items for each order - const ordersWithItems = await Promise.all( - userOrders.map(async (order) => { - const items = await db - .select() - .from(orderItems) - .where(eq(orderItems.orderId, order.id)); + if (userOrders.length === 0) { + return []; + } - return { - ...order, - items, - }; - }) - ); + // ⚡ Bolt Performance Optimization: Fix N+1 Query + // Fetch all order items in a single query instead of one query per order + const orderIds = userOrders.map(order => order.id); + const allItems = await db + .select() + .from(orderItems) + .where(inArray(orderItems.orderId, orderIds)); + + // Group items by order ID for O(1) lookup + const itemsByOrderId = new Map(); + for (const item of allItems) { + if (!itemsByOrderId.has(item.orderId)) { + itemsByOrderId.set(item.orderId, []); + } + itemsByOrderId.get(item.orderId)!.push(item); + } + + const ordersWithItems = userOrders.map(order => ({ + ...order, + items: itemsByOrderId.get(order.id) || [], + })); return ordersWithItems; }