← back to Wine Finder Next

lib/database-optimizations.ts

158 lines

// Database optimization queries and indexes

export const databaseOptimizations = {
  // Create indexes for better query performance
  createIndexes: [
    // Bottles collection
    `CREATE INDEX idx_bottles_status ON bottles(status)`,
    `CREATE INDEX idx_bottles_vintage ON bottles(vintage)`,
    `CREATE INDEX idx_bottles_available_units ON bottles(availableMembershipUnits)`,
    `CREATE INDEX idx_bottles_created ON bottles(createdAt DESC)`,
    `CREATE INDEX idx_bottles_compound ON bottles(status, availableMembershipUnits)`,

    // Membership units collection
    `CREATE INDEX idx_membership_bottle_user ON membershipUnits(bottleId, userId)`,
    `CREATE INDEX idx_membership_user ON membershipUnits(userId)`,
    `CREATE INDEX idx_membership_status ON membershipUnits(status)`,

    // Votes collection
    `CREATE INDEX idx_votes_bottle ON votes(bottleId)`,
    `CREATE INDEX idx_votes_status_date ON votes(status, endDate DESC)`,
    `CREATE INDEX idx_votes_user ON votes(userId)`,

    // Marketplace collection
    `CREATE INDEX idx_marketplace_status ON marketplace(status, listedAt DESC)`,
    `CREATE INDEX idx_marketplace_bottle ON marketplace(bottleId)`,
    `CREATE INDEX idx_marketplace_seller ON marketplace(sellerId)`,
    `CREATE INDEX idx_marketplace_price ON marketplace(price)`,

    // Price history collection
    `CREATE INDEX idx_price_wine ON priceHistory(wineName)`,
    `CREATE INDEX idx_price_date ON priceHistory(date DESC)`,
    `CREATE INDEX idx_price_compound ON priceHistory(wineName, date DESC)`,
  ],

  // Optimized queries with proper indexing
  optimizedQueries: {
    // Get active bottles with available units
    getActiveBottles: `
      SELECT id, name, vintage, producer, region, size, estimatedValue,
             totalMembershipUnits, availableMembershipUnits, status
      FROM bottles
      WHERE status = 'active' AND availableMembershipUnits > 0
      ORDER BY createdAt DESC
      LIMIT ?
    `,

    // Get user's membership units with bottle info
    getUserMemberships: `
      SELECT mu.*, b.name, b.vintage, b.producer, b.estimatedValue
      FROM membershipUnits mu
      JOIN bottles b ON mu.bottleId = b.id
      WHERE mu.userId = ?
      ORDER BY mu.acquiredAt DESC
    `,

    // Get active votes with vote counts
    getActiveVotesWithCounts: `
      SELECT v.*,
             COUNT(DISTINCT vd.userId) as uniqueVoters,
             SUM(CASE WHEN vd.choice = 'for' THEN 1 ELSE 0 END) as votesFor,
             SUM(CASE WHEN vd.choice = 'against' THEN 1 ELSE 0 END) as votesAgainst
      FROM votes v
      LEFT JOIN voteDetails vd ON v.id = vd.voteId
      WHERE v.status = 'active' AND v.endDate > NOW()
      GROUP BY v.id
      ORDER BY v.endDate ASC
    `,

    // Get marketplace listings with bottle details
    getActiveListings: `
      SELECT m.*, b.name, b.vintage, b.producer, b.region,
             u.name as sellerName
      FROM marketplace m
      JOIN bottles b ON m.bottleId = b.id
      JOIN users u ON m.sellerId = u.id
      WHERE m.status = 'available'
      ORDER BY m.listedAt DESC
      LIMIT ?
    `,

    // Aggregate statistics
    getBottleStats: `
      SELECT
        COUNT(*) as totalBottles,
        SUM(totalMembershipUnits) as totalUnits,
        SUM(totalMembershipUnits - availableMembershipUnits) as soldUnits,
        SUM(estimatedValue) as totalValue,
        AVG(estimatedValue) as avgValue
      FROM bottles
      WHERE status IN ('active', 'unlocked')
    `,
  },

  // Query optimization tips
  tips: {
    pagination: 'Use LIMIT and OFFSET for large result sets',
    projection: 'Only SELECT fields that are needed',
    joins: 'Use proper indexes on JOIN columns',
    aggregation: 'Pre-calculate aggregates when possible',
    caching: 'Cache frequently accessed, rarely changed data',
    batching: 'Batch multiple queries into single request',
    connection_pooling: 'Use connection pooling for better resource utilization',
  },
};

// Database connection pool configuration
export const connectionPoolConfig = {
  min: 2,
  max: 10,
  acquireTimeoutMillis: 30000,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
};

// Query result caching strategy
export const cachingStrategy = {
  // Static data - cache for longer
  staticData: {
    ttl: 3600, // 1 hour
    tables: ['regions', 'producers', 'wineTypes'],
  },

  // Semi-static data - moderate caching
  semiStaticData: {
    ttl: 300, // 5 minutes
    tables: ['bottles', 'membershipPlans'],
  },

  // Dynamic data - short cache
  dynamicData: {
    ttl: 60, // 1 minute
    tables: ['votes', 'marketplace', 'priceHistory'],
  },

  // Real-time data - no caching
  realtimeData: {
    ttl: 0,
    tables: ['transactions', 'userSessions'],
  },
};

// Database maintenance tasks
export const maintenanceTasks = {
  // Vacuum and analyze tables
  vacuum: 'VACUUM ANALYZE',

  // Update statistics
  updateStats: 'ANALYZE',

  // Clean old data
  cleanOldData: `
    DELETE FROM sessions WHERE expiresAt < NOW() - INTERVAL '30 days';
    DELETE FROM logs WHERE createdAt < NOW() - INTERVAL '90 days';
  `,

  // Optimize table storage
  optimizeTables: 'OPTIMIZE TABLE bottles, membershipUnits, votes, marketplace',
};