Exercise 2: Measuring an Index — Possible Solution ==================================================== 1. Seed a realistic volume (dev database only). createMany is far faster than 100,000 separate creates: const author = await prisma.user.create({ data: { email: "bulk@test.local" } }); for (let batch = 0; batch < 100; batch++) { await prisma.post.createMany({ data: Array.from({ length: 1000 }, (_, i) => { const n = batch * 1000 + i; return { title: `Post ${n}`, slug: `bulk-${n}`, authorId: author.id, published: n % 3 !== 0, createdAt: new Date(Date.now() - n * 60_000), }; }), }); } 2. Measure before adding the index: const plan = await prisma.$queryRaw` EXPLAIN ANALYZE SELECT "id", "title" FROM "Post" WHERE "published" = true ORDER BY "createdAt" DESC LIMIT 20 `; Typical result: Limit -> Sort -> Seq Scan on "Post" (reads all 100,000 rows, then sorts them) 3. Add the index and migrate: @@index([published, createdAt(sort: Desc)]) npx prisma migrate dev --name post-published-created-index 4. Measure again: Typical result: Limit -> Index Scan using "Post_published_createdAt_idx" (reads just the first 20 matching entries) What changed: before, PostgreSQL had to read every row and sort the matches itself; with the index, published posts are already stored in createdAt order, so it can read the first 20 entries and stop. The "actual time" figure typically drops by a large factor. Exact numbers and index names depend on your data and PostgreSQL version. WHY THIS WORKS AS AN ANSWER ------------------------------ It follows the chapter's rule — measure, change one thing, measure again — with enough data for the difference to be real (on a table of a few dozen rows PostgreSQL may reasonably ignore the index). The index columns match the query exactly: an equality filter first (published), then the sort column in the same direction (createdAt DESC).