Changeset b348db4 for database/drizzle/util/seed.ts
- Timestamp:
- 01/29/26 03:53:57 (5 months ago)
- Branches:
- main
- Children:
- c586cbd, f727252
- Parents:
- be22289
- File:
-
- 1 edited
-
database/drizzle/util/seed.ts (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
database/drizzle/util/seed.ts
rbe22289 rb348db4 1 1 import pg from 'pg'; 2 2 3 const seedSQL = `3 const dataSQL = ` 4 4 TRUNCATE TABLE 5 5 suggestions, … … 366 366 ` 367 367 368 async function main() { 368 const triggerSQL = ` 369 CREATE OR REPLACE FUNCTION update_build_total_price() 370 RETURNS TRIGGER 371 LANGUAGE plpgsql AS $$ 372 DECLARE 373 target_build_id INT; 374 BEGIN 375 IF (TG_OP = 'DELETE') THEN 376 target_build_id := OLD.build_id; 377 ELSE 378 target_build_id := NEW.build_id; 379 END IF; 380 381 UPDATE "build" 382 SET "total_price" = ( 383 SELECT COALESCE(SUM(c.price * bc.num_components), 0) 384 FROM "build_component" bc 385 JOIN "components" c ON bc.component_id = c.id 386 WHERE bc.build_id = target_build_id 387 ) 388 WHERE id = target_build_id; 389 390 RETURN NULL; 391 END; 392 $$; 393 394 DROP TRIGGER IF EXISTS trigger_auto_update_price ON "build_component"; 395 CREATE TRIGGER trigger_auto_update_price 396 AFTER INSERT OR UPDATE OR DELETE ON "build_component" 397 FOR EACH ROW 398 EXECUTE FUNCTION update_build_total_price(); 399 400 401 402 CREATE OR REPLACE FUNCTION check_review_validity() 403 RETURNS TRIGGER 404 LANGUAGE plpgsql AS $$ 405 DECLARE 406 build_owner_id INT; 407 BEGIN 408 SELECT user_id INTO build_owner_id FROM "build" WHERE id = NEW.build_id; 409 410 IF NEW.user_id = build_owner_id THEN 411 RAISE EXCEPTION 'Cannot review own builds.'; 412 END IF; 413 414 RETURN NEW; 415 END; 416 $$; 417 418 DROP TRIGGER IF EXISTS trigger_check_self_review ON "review"; 419 CREATE TRIGGER trigger_check_self_review 420 BEFORE INSERT ON "review" 421 FOR EACH ROW 422 EXECUTE FUNCTION check_review_validity(); 423 424 425 426 CREATE OR REPLACE FUNCTION check_rating_validity() 427 RETURNS TRIGGER 428 LANGUAGE plpgsql AS $$ 429 DECLARE 430 build_owner_id INT; 431 BEGIN 432 SELECT user_id INTO build_owner_id FROM "build" WHERE id = NEW.build_id; 433 434 IF NEW.user_id = build_owner_id THEN 435 RAISE EXCEPTION 'Cannot rate own builds.'; 436 END IF; 437 438 RETURN NEW; 439 END; 440 $$; 441 442 DROP TRIGGER IF EXISTS trigger_check_self_rating ON "rating_build"; 443 CREATE TRIGGER trigger_check_self_rating 444 BEFORE INSERT ON "rating_build" 445 FOR EACH ROW 446 EXECUTE FUNCTION check_rating_validity(); 447 ` 448 449 const functionSQL = ` 450 CREATE OR REPLACE FUNCTION get_report_top_components() 451 RETURNS TABLE ( 452 type TEXT, 453 brand TEXT, 454 name TEXT, 455 usage_count BIGINT, 456 avg_build_rating NUMERIC 457 ) 458 LANGUAGE sql 459 AS $$ 460 SELECT 461 c.type, 462 c.brand, 463 c.name, 464 COUNT(bc.component_id) AS usage_count, 465 AVG(rb.value) AS avg_build_rating 466 FROM components c 467 JOIN build_component bc ON c.id = bc.component_id 468 JOIN build b ON bc.build_id = b.id 469 JOIN rating_build rb ON b.id = rb.build_id 470 WHERE b.created_at >= CURRENT_DATE - INTERVAL '1 year' 471 GROUP BY c.type, c.brand, c.name 472 HAVING AVG(rb.value) >= 4.5 473 ORDER BY usage_count DESC, avg_build_rating DESC 474 LIMIT 15; 475 $$; 476 477 478 479 CREATE OR REPLACE FUNCTION get_report_user_reputation_leaderboard() 480 RETURNS TABLE ( 481 username TEXT, 482 email TEXT, 483 approved_builds_count BIGINT, 484 total_favorites_received BIGINT, 485 avg_rating_received NUMERIC, 486 reputation_score NUMERIC 487 ) 488 LANGUAGE sql 489 AS $$ 490 WITH build_stats AS ( 491 SELECT 492 b.id AS build_id, 493 b.user_id, 494 COUNT(DISTINCT fb.user_id) AS favorites_count, 495 AVG(rb.value) AS avg_rating 496 FROM build b 497 LEFT JOIN favorite_build fb ON b.id = fb.build_id 498 LEFT JOIN rating_build rb ON b.id = rb.build_id 499 WHERE b.is_approved = TRUE 500 GROUP BY b.id, b.user_id 501 ), 502 user_stats AS ( 503 SELECT 504 user_id, 505 COUNT(build_id) AS approved_builds_count, 506 COALESCE(SUM(favorites_count), 0) AS total_favorites_received, 507 AVG(avg_rating) AS avg_rating_received 508 FROM build_stats 509 GROUP BY user_id 510 ) 511 SELECT 512 u.username, 513 u.email, 514 us.approved_builds_count, 515 us.total_favorites_received, 516 ROUND(CAST(COALESCE(us.avg_rating_received, 0) AS numeric), 2) AS avg_rating_received, 517 ( 518 (us.approved_builds_count * 10) + 519 (us.total_favorites_received * 5) + 520 (COALESCE(us.avg_rating_received, 0) * 20) 521 ) AS reputation_score 522 FROM user_stats us 523 JOIN users u ON u.id = us.user_id 524 ORDER BY reputation_score DESC 525 LIMIT 10; 526 $$; 527 528 529 530 CREATE OR REPLACE FUNCTION get_report_price_to_performance() 531 RETURNS TABLE ( 532 build_name TEXT, 533 cpu_model TEXT, 534 gpu_model TEXT, 535 total_price NUMERIC, 536 performance_score NUMERIC, 537 price_to_performance_index NUMERIC 538 ) 539 LANGUAGE sql 540 AS $$ 541 WITH cpu_per_build AS ( 542 SELECT 543 b.id AS build_id, 544 c.name AS cpu_model, 545 cpu.cores, 546 cpu.base_clock 547 FROM build b 548 JOIN build_component bc ON b.id = bc.build_id 549 JOIN components c ON bc.component_id = c.id 550 JOIN cpu ON c.id = cpu.component_id 551 WHERE LOWER(c.type) = LOWER('CPU') 552 ), 553 gpu_per_build AS ( 554 SELECT 555 b.id AS build_id, 556 c.name AS gpu_model, 557 gpu.vram 558 FROM build b 559 JOIN build_component bc ON b.id = bc.build_id 560 JOIN components c ON bc.component_id = c.id 561 JOIN gpu ON c.id = gpu.component_id 562 WHERE LOWER(c.type) = LOWER('GPU') 563 ) 564 SELECT 565 b.name AS build_name, 566 cpu.cpu_model, 567 gpu.gpu_model, 568 b.total_price, 569 (cpu.cores * cpu.base_clock + gpu.vram * 100) AS performance_score, 570 ROUND( 571 CAST( 572 (cpu.cores * cpu.base_clock + gpu.vram * 100) / NULLIF(b.total_price, 0) 573 AS numeric), 574 4 575 ) AS price_to_performance_index 576 FROM build b 577 JOIN cpu_per_build cpu ON b.id = cpu.build_id 578 JOIN gpu_per_build gpu ON b.id = gpu.build_id 579 WHERE b.total_price > 0 580 ORDER BY price_to_performance_index DESC 581 LIMIT 20; 582 $$; 583 584 585 586 CREATE OR REPLACE FUNCTION get_report_budget_tier_popularity() 587 RETURNS TABLE ( 588 price_tier TEXT, 589 builds_count BIGINT, 590 avg_favorites NUMERIC, 591 avg_rating NUMERIC, 592 unique_builders BIGINT, 593 engagement_score NUMERIC 594 ) 595 LANGUAGE sql 596 AS $$ 597 WITH price_tier_builds AS ( 598 SELECT 599 b.id AS build_id, 600 b.user_id, 601 b.total_price, 602 CASE 603 WHEN b.total_price < 500 THEN 'Budget' 604 WHEN b.total_price < 1000 THEN 'Mid-Range' 605 WHEN b.total_price < 2000 THEN 'High-End' 606 ELSE 'Enthusiast' 607 END AS price_tier 608 FROM build b 609 WHERE b.is_approved = TRUE 610 AND b.created_at >= CURRENT_DATE - INTERVAL '6 months' 611 ), 612 favorites AS ( 613 SELECT 614 build_id, 615 COUNT(DISTINCT user_id) AS favorites_count 616 FROM favorite_build 617 GROUP BY build_id 618 ), 619 engagement_stats AS ( 620 SELECT 621 ptb.price_tier, 622 COUNT(DISTINCT ptb.build_id) AS builds_count, 623 AVG(COALESCE(f.favorites_count, 0)) AS avg_favorites, 624 AVG(rb.value) AS avg_rating, 625 COUNT(DISTINCT ptb.user_id) AS unique_builders 626 FROM price_tier_builds ptb 627 LEFT JOIN rating_build rb ON ptb.build_id = rb.build_id 628 LEFT JOIN favorites f ON ptb.build_id = f.build_id 629 GROUP BY ptb.price_tier 630 ) 631 SELECT 632 price_tier, 633 builds_count, 634 ROUND(CAST(avg_favorites AS numeric), 1) AS avg_favorites, 635 ROUND(CAST(COALESCE(avg_rating, 0) AS numeric), 2) AS avg_rating, 636 unique_builders, 637 ROUND( 638 CAST( 639 (builds_count * 2) + 640 (avg_favorites * 3) + 641 (COALESCE(avg_rating, 0) * 10) + 642 (unique_builders * 1.5) 643 AS numeric), 644 2 645 ) AS engagement_score 646 FROM engagement_stats 647 ORDER BY engagement_score DESC 648 LIMIT 15; 649 $$; 650 651 652 653 CREATE OR REPLACE FUNCTION get_report_compatibility() 654 RETURNS TABLE ( 655 cpu_combo TEXT, 656 motherboard_chipset TEXT, 657 total_builds BIGINT, 658 avg_satisfaction NUMERIC, 659 success_rate NUMERIC 660 ) 661 LANGUAGE sql 662 AS $$ 663 WITH cpu_mobo_pairs AS ( 664 SELECT 665 cpu_comp.brand AS cpu_brand, 666 cpu_comp.name AS cpu_model, 667 mobo.chipset AS motherboard_chipset, 668 b.id AS build_id, 669 b.user_id, 670 b.created_at 671 FROM build b 672 JOIN build_component bc_cpu ON b.id = bc_cpu.build_id 673 JOIN components cpu_comp ON bc_cpu.component_id = cpu_comp.id 674 JOIN cpu ON cpu.component_id = cpu_comp.id 675 JOIN build_component bc_mobo ON b.id = bc_mobo.build_id 676 JOIN components mobo_comp ON bc_mobo.component_id = mobo_comp.id 677 JOIN motherboard mobo ON mobo.component_id = mobo_comp.id 678 WHERE b.is_approved = TRUE 679 ), 680 recent_activity AS ( 681 SELECT DISTINCT build_id 682 FROM review 683 WHERE created_at >= CURRENT_DATE - INTERVAL '3 months' 684 ), 685 pair_metrics AS ( 686 SELECT 687 cmp.cpu_brand, 688 cmp.cpu_model, 689 cmp.motherboard_chipset, 690 COUNT(DISTINCT cmp.build_id) AS total_builds, 691 AVG(COALESCE(rb.value, 0)) AS avg_satisfaction, 692 COUNT(DISTINCT ra.build_id) AS active_builds 693 FROM cpu_mobo_pairs cmp 694 LEFT JOIN rating_build rb ON cmp.build_id = rb.build_id 695 LEFT JOIN recent_activity ra ON cmp.build_id = ra.build_id 696 GROUP BY 697 cmp.cpu_brand, 698 cmp.cpu_model, 699 cmp.motherboard_chipset 700 HAVING COUNT(DISTINCT cmp.build_id) >= 2 701 ) 702 SELECT 703 CONCAT(cpu_brand, ' ', cpu_model) AS cpu_combo, 704 motherboard_chipset, 705 total_builds, 706 ROUND(CAST(avg_satisfaction AS numeric), 2) AS avg_satisfaction, 707 ROUND( 708 CAST( 709 (avg_satisfaction / 5.0) * 0.6 + 710 (CAST(active_builds AS DECIMAL) / total_builds) * 0.4 711 AS numeric), 712 3 713 ) AS success_rate 714 FROM pair_metrics 715 ORDER BY success_rate DESC, total_builds DESC 716 LIMIT 15; 717 $$; 718 719 720 721 CREATE OR REPLACE FUNCTION get_report_storage_optimization() 722 RETURNS TABLE ( 723 config_type TEXT, 724 ssd_brand TEXT, 725 builds_count BIGINT, 726 avg_storage_cost NUMERIC, 727 avg_total_capacity_gb NUMERIC, 728 avg_build_rating NUMERIC, 729 storage_cost_pct NUMERIC, 730 optimization_score NUMERIC 731 ) 732 LANGUAGE sql 733 AS $$ 734 WITH storage_configs AS ( 735 SELECT 736 b.id AS build_id, 737 b.total_price, 738 739 ssd_comp.brand AS ssd_brand, 740 ssd_storage.capacity AS ssd_capacity, 741 ssd_comp.price AS ssd_price, 742 743 hdd_storage.capacity AS hdd_capacity, 744 hdd_comp.price AS hdd_price, 745 746 CASE 747 WHEN hdd_storage.capacity IS NULL THEN 'SSD-Only' 748 WHEN ssd_storage.capacity < 512 THEN 'SSD-Boot-HDD-Storage' 749 ELSE 'SSD-Primary-HDD-Archive' 750 END AS config_type 751 FROM build b 752 JOIN build_component bc_ssd ON b.id = bc_ssd.build_id 753 JOIN components ssd_comp ON bc_ssd.component_id = ssd_comp.id 754 JOIN storage ssd_storage ON ssd_storage.component_id = ssd_comp.id 755 756 LEFT JOIN build_component bc_hdd ON b.id = bc_hdd.build_id 757 LEFT JOIN components hdd_comp ON bc_hdd.component_id = hdd_comp.id 758 LEFT JOIN storage hdd_storage ON hdd_storage.component_id = hdd_comp.id 759 760 WHERE ssd_comp.type = 'storage' 761 AND b.is_approved = TRUE 762 AND b.created_at >= CURRENT_DATE - INTERVAL '1 year' 763 764 AND ( 765 ssd_storage.type ILIKE '%nvme%' 766 OR ssd_storage.type ILIKE '%ssd%' 767 OR ssd_storage.type ILIKE '%m.2%' 768 OR ssd_storage.form_factor ILIKE '%m.2%' 769 ) 770 AND ( 771 hdd_storage.component_id IS NULL 772 OR hdd_storage.type ILIKE '%hdd%' 773 OR hdd_storage.form_factor ILIKE '%3.5%' 774 ) 775 ), 776 config_performance AS ( 777 SELECT 778 sc.config_type, 779 sc.ssd_brand, 780 COUNT(sc.build_id) AS builds_count, 781 AVG(sc.ssd_price + COALESCE(sc.hdd_price, 0)) AS avg_storage_cost, 782 AVG(sc.ssd_capacity + COALESCE(sc.hdd_capacity, 0)) AS avg_total_capacity, 783 AVG(rb.value) AS avg_build_rating, 784 AVG((sc.ssd_price + COALESCE(sc.hdd_price, 0)) / NULLIF(sc.total_price, 0)) AS storage_cost_ratio 785 FROM storage_configs sc 786 LEFT JOIN rating_build rb ON sc.build_id = rb.build_id 787 GROUP BY sc.config_type, sc.ssd_brand 788 HAVING COUNT(sc.build_id) >= 2 789 ) 790 SELECT 791 config_type, 792 ssd_brand, 793 builds_count, 794 ROUND(CAST(avg_storage_cost AS numeric), 2) AS avg_storage_cost, 795 ROUND(CAST(avg_total_capacity AS numeric), 0) AS avg_total_capacity_gb, 796 ROUND(CAST(COALESCE(avg_build_rating, 0) AS numeric), 2) AS avg_build_rating, 797 ROUND(CAST(COALESCE(storage_cost_ratio, 0) * 100 AS numeric), 1) AS storage_cost_pct, 798 ROUND( 799 CAST( 800 (COALESCE(avg_build_rating, 0) / 5.0 * 40) + 801 (avg_total_capacity / NULLIF(avg_storage_cost, 0) * 0.5) + 802 ((1 - COALESCE(storage_cost_ratio, 0)) * 30) + 803 (LN(CAST(builds_count + 1 AS numeric)) * 5) 804 AS numeric), 805 2 806 ) AS optimization_score 807 FROM config_performance 808 ORDER BY optimization_score DESC, builds_count DESC 809 LIMIT 15; 810 $$; 811 ` 812 813 async function seed() { 369 814 if (!process.env.DATABASE_URL) { 370 815 throw new Error('DATABASE_URL environment variable not set'); … … 383 828 const count = parseInt(result.rows[0].count); 384 829 385 if (count > 0) { 386 console.log('Database already seeded, skipping...'); 387 return; 830 if (count === 0) { 831 console.log('Seeding initial data...'); 832 await client.query(dataSQL); 833 console.log('Data seeded successfully.'); 834 } else { 835 console.log('Database already contains data, skipping data seed.'); 388 836 } 389 837 390 console.log('Seeding database...'); 391 392 await client.query(seedSQL); 838 console.log('Applying Database Triggers...'); 839 await client.query(triggerSQL); 840 841 console.log('Applying Database Functions...'); 842 await client.query(functionSQL); 393 843 394 844 console.log('Database seeded successfully...'); … … 401 851 } 402 852 403 main().catch((err) => {853 seed().catch((err) => { 404 854 console.error(err); 405 855 process.exit(1);
Note:
See TracChangeset
for help on using the changeset viewer.
