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 <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-08 13:10:39 +07:00
parent 5848c2b386
commit 91ef71d5e1
4 changed files with 25 additions and 15 deletions

View File

@@ -220,21 +220,22 @@ export class PrismaAdminQueryRepository implements IAdminQueryRepository {
if (!user) return null; if (!user) return null;
const transactionsCount = await this.prisma.transaction.count({ const [transactionsCount, recentListings] = await Promise.all([
where: { buyerId: userId }, this.prisma.transaction.count({
}); where: { buyerId: userId },
}),
const recentListings = await this.prisma.listing.findMany({ this.prisma.listing.findMany({
where: { sellerId: userId }, where: { sellerId: userId },
select: { select: {
id: true, id: true,
status: true, status: true,
createdAt: true, createdAt: true,
property: { select: { title: true } }, property: { select: { title: true } },
}, },
orderBy: { createdAt: 'desc' }, orderBy: { createdAt: 'desc' },
take: 10, take: 10,
}); }),
]);
const recentActivity = recentListings.map((l) => ({ const recentActivity = recentListings.map((l) => ({
type: 'listing', type: 'listing',

View File

@@ -17,6 +17,7 @@ export class PrismaValuationRepository implements IValuationRepository {
const records = await this.prisma.valuation.findMany({ const records = await this.prisma.valuation.findMany({
where: { propertyId }, where: { propertyId },
orderBy: { createdAt: 'desc' }, orderBy: { createdAt: 'desc' },
take: 50,
}); });
return records.map((r) => this.toDomain(r)); return records.map((r) => this.toDomain(r));
} }

View File

@@ -78,6 +78,7 @@ export class PrismaPropertyRepository implements IPropertyRepository {
const mediaList = await this.prisma.propertyMedia.findMany({ const mediaList = await this.prisma.propertyMedia.findMany({
where: { propertyId }, where: { propertyId },
orderBy: { order: 'asc' }, orderBy: { order: 'asc' },
take: 50,
}); });
return mediaList.map((m) => this.toMediaDomain(m)); return mediaList.map((m) => this.toMediaDomain(m));
} }

View File

@@ -57,6 +57,9 @@ model User {
@@index([phone]) @@index([phone])
@@index([role]) @@index([role])
@@index([kycStatus])
@@index([isActive])
@@index([createdAt])
} }
enum OAuthProvider { enum OAuthProvider {
@@ -245,6 +248,9 @@ model Listing {
@@index([propertyId]) @@index([propertyId])
@@index([agentId]) @@index([agentId])
@@index([publishedAt]) @@index([publishedAt])
@@index([createdAt])
@@index([featuredUntil])
@@index([expiresAt])
} }
// ============================================================================= // =============================================================================
@@ -378,6 +384,7 @@ model Payment {
@@index([transactionId]) @@index([transactionId])
@@index([status]) @@index([status])
@@index([providerTxId]) @@index([providerTxId])
@@index([createdAt])
} }
// ============================================================================= // =============================================================================