← back to Handbag Auth Nextjs

scripts/optimize-database.ts

125 lines

#!/usr/bin/env ts-node
// Database Optimization Script
// Creates indexes for faster queries

import Database from 'better-sqlite3'
import path from 'path'

const DB_PATH = path.join(process.cwd(), 'data', 'handbags.db')

console.log('🔧 Optimizing database...')
console.log(`Database: ${DB_PATH}\n`)

const db = new Database(DB_PATH)

try {
  // Enable Write-Ahead Logging for better performance
  console.log('⚙️  Enabling WAL mode...')
  db.pragma('journal_mode = WAL')

  // Create indexes for frequently queried columns
  console.log('📊 Creating indexes...')

  // Index on brand (most common filter)
  console.log('  - Creating index on brand...')
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_listings_brand
    ON listings(brand)
  `)

  // Index on price_usd for sorting and filtering
  console.log('  - Creating index on price_usd...')
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_listings_price
    ON listings(price_usd)
  `)

  // Index on is_active for filtering active listings
  console.log('  - Creating index on is_active...')
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_listings_active
    ON listings(is_active)
  `)

  // Composite index for common query pattern (active + price range + brand)
  console.log('  - Creating composite index...')
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_listings_active_price_brand
    ON listings(is_active, price_usd, brand)
  `)

  // Index on crawled_at for finding latest data
  console.log('  - Creating index on crawled_at...')
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_listings_crawled
    ON listings(crawled_at DESC)
  `)

  // Optimize database file
  console.log('\n🗜️  Running VACUUM to optimize database file...')
  db.exec('VACUUM')

  // Analyze tables for query planner
  console.log('📈 Running ANALYZE to update statistics...')
  db.exec('ANALYZE')

  // Get database stats
  const stats = db.prepare(`
    SELECT
      COUNT(*) as total_listings,
      COUNT(DISTINCT brand) as total_brands,
      (SELECT name FROM sqlite_master WHERE type='index') as sample_index
    FROM listings
  `).get() as any

  console.log('\n✅ Optimization complete!')
  console.log(`   Total listings: ${stats.total_listings.toLocaleString()}`)
  console.log(`   Total brands: ${stats.total_brands}`)

  // Show all indexes
  const indexes = db.prepare(`
    SELECT name, sql
    FROM sqlite_master
    WHERE type = 'index'
      AND tbl_name = 'listings'
      AND name NOT LIKE 'sqlite_%'
  `).all()

  console.log('\n📑 Active indexes:')
  indexes.forEach((idx: any) => {
    console.log(`   - ${idx.name}`)
  })

  // Test query performance
  console.log('\n⚡ Testing query performance...')

  const testStart = Date.now()
  db.prepare(`
    SELECT COUNT(*)
    FROM listings
    WHERE is_active = 1
      AND price_usd > 1000
      AND price_usd < 100000
      AND brand = 'Hermes'
  `).get()
  const testDuration = Date.now() - testStart

  console.log(`   Query time: ${testDuration}ms`)

  if (testDuration < 10) {
    console.log('   ✅ Excellent performance!')
  } else if (testDuration < 50) {
    console.log('   ✓ Good performance')
  } else {
    console.log('   ⚠️  Could be faster')
  }

} catch (error) {
  console.error('❌ Error optimizing database:', error)
  process.exit(1)
} finally {
  db.close()
}

console.log('\n✨ Database optimization complete!\n')