From 91ef71d5e1ed3df2c198d6134ca571608a83bfe4 Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Wed, 8 Apr 2026 13:10:39 +0700 Subject: [PATCH] fix(db): add missing indexes, bound unbounded queries, parallelize admin queries - Add 7 missing indexes: User(kycStatus, isActive, createdAt), Listing(createdAt, featuredUntil, expiresAt), Payment(createdAt) - Add take:50 limit to unbounded findMediaByPropertyId and findByPropertyId - Parallelize sequential queries in getUserDetail with Promise.all Co-Authored-By: Paperclip --- .../prisma-admin-query.repository.ts | 31 ++++++++++--------- .../prisma-valuation.repository.ts | 1 + .../prisma-property.repository.ts | 1 + prisma/schema.prisma | 7 +++++ 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/apps/api/src/modules/admin/infrastructure/repositories/prisma-admin-query.repository.ts b/apps/api/src/modules/admin/infrastructure/repositories/prisma-admin-query.repository.ts index 7d58636..085f760 100644 --- a/apps/api/src/modules/admin/infrastructure/repositories/prisma-admin-query.repository.ts +++ b/apps/api/src/modules/admin/infrastructure/repositories/prisma-admin-query.repository.ts @@ -220,21 +220,22 @@ export class PrismaAdminQueryRepository implements IAdminQueryRepository { if (!user) return null; - const transactionsCount = await this.prisma.transaction.count({ - where: { buyerId: userId }, - }); - - const recentListings = await this.prisma.listing.findMany({ - where: { sellerId: userId }, - select: { - id: true, - status: true, - createdAt: true, - property: { select: { title: true } }, - }, - orderBy: { createdAt: 'desc' }, - take: 10, - }); + const [transactionsCount, recentListings] = await Promise.all([ + this.prisma.transaction.count({ + where: { buyerId: userId }, + }), + this.prisma.listing.findMany({ + where: { sellerId: userId }, + select: { + id: true, + status: true, + createdAt: true, + property: { select: { title: true } }, + }, + orderBy: { createdAt: 'desc' }, + take: 10, + }), + ]); const recentActivity = recentListings.map((l) => ({ type: 'listing', diff --git a/apps/api/src/modules/analytics/infrastructure/repositories/prisma-valuation.repository.ts b/apps/api/src/modules/analytics/infrastructure/repositories/prisma-valuation.repository.ts index a92277c..63c53e6 100644 --- a/apps/api/src/modules/analytics/infrastructure/repositories/prisma-valuation.repository.ts +++ b/apps/api/src/modules/analytics/infrastructure/repositories/prisma-valuation.repository.ts @@ -17,6 +17,7 @@ export class PrismaValuationRepository implements IValuationRepository { const records = await this.prisma.valuation.findMany({ where: { propertyId }, orderBy: { createdAt: 'desc' }, + take: 50, }); return records.map((r) => this.toDomain(r)); } diff --git a/apps/api/src/modules/listings/infrastructure/repositories/prisma-property.repository.ts b/apps/api/src/modules/listings/infrastructure/repositories/prisma-property.repository.ts index e1d58e9..0f35043 100644 --- a/apps/api/src/modules/listings/infrastructure/repositories/prisma-property.repository.ts +++ b/apps/api/src/modules/listings/infrastructure/repositories/prisma-property.repository.ts @@ -78,6 +78,7 @@ export class PrismaPropertyRepository implements IPropertyRepository { const mediaList = await this.prisma.propertyMedia.findMany({ where: { propertyId }, orderBy: { order: 'asc' }, + take: 50, }); return mediaList.map((m) => this.toMediaDomain(m)); } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3aa45a5..2726f22 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -57,6 +57,9 @@ model User { @@index([phone]) @@index([role]) + @@index([kycStatus]) + @@index([isActive]) + @@index([createdAt]) } enum OAuthProvider { @@ -245,6 +248,9 @@ model Listing { @@index([propertyId]) @@index([agentId]) @@index([publishedAt]) + @@index([createdAt]) + @@index([featuredUntil]) + @@index([expiresAt]) } // ============================================================================= @@ -378,6 +384,7 @@ model Payment { @@index([transactionId]) @@index([status]) @@index([providerTxId]) + @@index([createdAt]) } // =============================================================================