Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-05-18 - [Fix N+1 Query in Order Fetching]
**Learning:** Performing database queries inside an array `map` block paired with `Promise.all` can create extreme inefficiencies, particularly on lists of arbitrary sizes (N+1 Query Issue).
**Action:** Replace `Promise.all` loops fetching related items with a singular SQL `inArray` query grouping items correctly in memory.
39 changes: 24 additions & 15 deletions lib/actions/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -297,22 +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,
};
})
);
// Fetch items for all orders in a single query to fix N+1 problem
// Performance Impact: O(N) queries -> O(1) queries (exactly 1 for items)
const orderIds = userOrders.map(order => order.id);
const allOrderItems = await db
.select()
.from(orderItems)
.where(inArray(orderItems.orderId, orderIds));

return ordersWithItems;
// Group items by order ID in memory
const itemsByOrderId = allOrderItems.reduce((acc, item) => {
if (!acc[item.orderId]) {
acc[item.orderId] = [];
}
acc[item.orderId].push(item);
return acc;
}, {} as Record<string, typeof allOrderItems>);

return userOrders.map(order => ({
...order,
items: itemsByOrderId[order.id] || [],
}));
}

// ============================================
Expand Down