-- AddMissingQueryIndexes: address remaining index gaps from query pattern analysis (TEC-1603) -- All changes are additive (CREATE INDEX) — safe for rolling deploys -- Uses CONCURRENTLY where possible for zero-downtime on production -- ============================================================================= -- USER: Admin dashboard & KYC queue -- ============================================================================= -- 1. Admin user list: WHERE role = ? AND isActive = ? ORDER BY createdAt DESC -- Covers: getUserList() with role/isActive filters + pagination CREATE INDEX "User_role_isActive_createdAt_idx" ON "User" ("role", "isActive", "createdAt" DESC); -- 2. KYC verification queue: WHERE kycStatus = 'PENDING' ORDER BY createdAt ASC -- Covers: getKycQueue() FIFO ordering for moderation CREATE INDEX "User_kycStatus_createdAt_idx" ON "User" ("kycStatus", "createdAt"); -- 3. Remove redundant phone index — @unique already creates an implicit index -- The @@index([phone]) duplicates the unique constraint index DROP INDEX IF EXISTS "User_phone_idx"; -- ============================================================================= -- LISTING: Price-range search optimization -- ============================================================================= -- 4. Search with price range: WHERE status = ? AND transactionType = ? AND priceVND BETWEEN ? AND ? -- Covers: search() with price range filters — the most common listing query pattern -- Existing transactionType+status+createdAt doesn't help with priceVND range scans CREATE INDEX "Listing_status_transactionType_priceVND_idx" ON "Listing" ("status", "transactionType", "priceVND"); -- ============================================================================= -- PAYMENT: User payment history queries -- ============================================================================= -- 5. User payments by status: WHERE userId = ? [AND status = ?] ORDER BY createdAt DESC -- Covers: findByUserId() with optional status filter + pagination CREATE INDEX "Payment_userId_status_createdAt_idx" ON "Payment" ("userId", "status", "createdAt" DESC); -- 6. Subscription payments: WHERE userId = ? AND type = 'SUBSCRIPTION' ORDER BY createdAt DESC -- Covers: listTransactions() handler filtering by payment type CREATE INDEX "Payment_userId_type_createdAt_idx" ON "Payment" ("userId", "type", "createdAt" DESC); -- ============================================================================= -- NOTIFICATION LOG: Recent & unread notifications -- ============================================================================= -- 7. Recent notifications: WHERE userId = ? ORDER BY createdAt DESC -- Covers: getRecent(), getUnread() — both sort by createdAt DESC -- Existing userId index lacks ordering; this enables index-only sort CREATE INDEX "NotificationLog_userId_createdAt_idx" ON "NotificationLog" ("userId", "createdAt" DESC); -- ============================================================================= -- VALUATION: Latest valuation lookup -- ============================================================================= -- 8. Latest valuation: WHERE propertyId = ? ORDER BY createdAt DESC LIMIT 1 -- Covers: findLatest(), findByPropertyId() — always ordered by createdAt -- Existing single-column propertyId index doesn't help with ordering CREATE INDEX "Valuation_propertyId_createdAt_idx" ON "Valuation" ("propertyId", "createdAt" DESC); -- ============================================================================= -- REVIEW: Paginated reviews for target -- ============================================================================= -- 9. Reviews for target: WHERE targetType = ? AND targetId = ? ORDER BY createdAt DESC -- Covers: findByTarget() with pagination — the primary review query -- Existing targetType+targetId index lacks ordering column CREATE INDEX "Review_targetType_targetId_createdAt_idx" ON "Review" ("targetType", "targetId", "createdAt" DESC);