perf: Optimize dashboard queries for faster load times

- Use pg_stat for approximate product count (instant vs full scan)
- LIMIT on DISTINCT queries for brand/category counts
- Single combined query (reduces round trips)
- Add index on store_product_snapshots.captured_at
- Add index on worker_tasks.worker_id and created_at

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kelly
2025-12-13 01:09:02 -07:00
parent 291a8279bd
commit 271faf0f00
3 changed files with 33 additions and 54 deletions

View File

@@ -14,63 +14,31 @@ router.use(authMiddleware);
/**
* GET /api/markets/dashboard
* Dashboard summary with counts for dispensaries, products, brands, etc.
* Optimized: Uses single query with approximate counts for large tables
*/
router.get('/dashboard', async (req: Request, res: Response) => {
try {
// Get dispensary count
const { rows: dispRows } = await pool.query(
`SELECT COUNT(*) as count FROM dispensaries`
);
// Get product count from store_products (canonical) or fallback to dutchie_products
const { rows: productRows } = await pool.query(`
SELECT COUNT(*) as count FROM store_products
`);
// Get brand count
const { rows: brandRows } = await pool.query(`
SELECT COUNT(DISTINCT brand_name_raw) as count
FROM store_products
WHERE brand_name_raw IS NOT NULL
`);
// Get category count
const { rows: categoryRows } = await pool.query(`
SELECT COUNT(DISTINCT category_raw) as count
FROM store_products
WHERE category_raw IS NOT NULL
`);
// Get snapshot count in last 24 hours
const { rows: snapshotRows } = await pool.query(`
SELECT COUNT(*) as count
FROM store_product_snapshots
WHERE captured_at >= NOW() - INTERVAL '24 hours'
`);
// Get last crawl time
const { rows: lastCrawlRows } = await pool.query(`
SELECT MAX(completed_at) as last_crawl
FROM crawl_orchestration_traces
WHERE success = true
`);
// Get failed job count (jobs in last 24h that failed)
const { rows: failedRows } = await pool.query(`
SELECT COUNT(*) as count
FROM crawl_orchestration_traces
WHERE success = false
AND started_at >= NOW() - INTERVAL '24 hours'
// Single optimized query for all counts
const { rows } = await pool.query(`
SELECT
(SELECT COUNT(*) FROM dispensaries) as dispensary_count,
(SELECT n_live_tup FROM pg_stat_user_tables WHERE relname = 'store_products') as product_count,
(SELECT COUNT(*) FROM (SELECT DISTINCT brand_name_raw FROM store_products WHERE brand_name_raw IS NOT NULL LIMIT 10000) b) as brand_count,
(SELECT COUNT(*) FROM (SELECT DISTINCT category_raw FROM store_products WHERE category_raw IS NOT NULL LIMIT 1000) c) as category_count,
(SELECT COUNT(*) FROM store_product_snapshots WHERE captured_at >= NOW() - INTERVAL '24 hours') as snapshot_count_24h,
(SELECT MAX(completed_at) FROM crawl_orchestration_traces WHERE success = true) as last_crawl,
(SELECT COUNT(*) FROM crawl_orchestration_traces WHERE success = false AND started_at >= NOW() - INTERVAL '24 hours') as failed_count
`);
const r = rows[0];
res.json({
dispensaryCount: parseInt(dispRows[0]?.count || '0', 10),
productCount: parseInt(productRows[0]?.count || '0', 10),
brandCount: parseInt(brandRows[0]?.count || '0', 10),
categoryCount: parseInt(categoryRows[0]?.count || '0', 10),
snapshotCount24h: parseInt(snapshotRows[0]?.count || '0', 10),
lastCrawlTime: lastCrawlRows[0]?.last_crawl || null,
failedJobCount: parseInt(failedRows[0]?.count || '0', 10),
dispensaryCount: parseInt(r?.dispensary_count || '0', 10),
productCount: parseInt(r?.product_count || '0', 10),
brandCount: parseInt(r?.brand_count || '0', 10),
categoryCount: parseInt(r?.category_count || '0', 10),
snapshotCount24h: parseInt(r?.snapshot_count_24h || '0', 10),
lastCrawlTime: r?.last_crawl || null,
failedJobCount: parseInt(r?.failed_count || '0', 10),
});
} catch (error: any) {
console.error('[Markets] Error fetching dashboard:', error.message);