nordabiz/database/migrations/004_seo_metrics.sql
Maciej Pienczyn 8f5bb9a47c auto-claude: 1.2 - Create database/migrations/004_seo_metrics.sql
Add SQL migration for SEO quality assessment metrics:
- PageSpeed Insights scores (seo, performance, accessibility, best_practices)
- PageSpeed audits JSONB column for detailed results
- On-page SEO: headings (h1/h2/h3), images with/without alt, links
- Structured data detection (JSON-LD, Microdata, RDFa)
- Technical SEO: canonical URLs, indexability, Core Web Vitals
- Open Graph & Twitter Cards meta tags
- SEO audit metadata (version, timestamp, errors, scores)
- Created v_company_seo_overview view for admin dashboard
- Added indexes for SEO-related queries

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:54:32 +01:00

191 lines
10 KiB
SQL

-- ============================================================
-- NordaBiz - Migration 004: SEO Quality Assessment Metrics
-- ============================================================
-- Created: 2026-01-08
-- Description:
-- - Extends company_website_analysis with PageSpeed Insights scores
-- - Adds on-page SEO detail columns
-- - Adds technical SEO fields
-- - Adds audit metadata for tracking
--
-- Usage:
-- PostgreSQL: psql -h localhost -U nordabiz_app -d nordabiz -f 004_seo_metrics.sql
-- SQLite: sqlite3 nordabiz_local.db < 004_seo_metrics.sql
-- ============================================================
-- ============================================================
-- 1. PAGESPEED INSIGHTS SCORES (0-100)
-- ============================================================
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS pagespeed_seo_score INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS pagespeed_performance_score INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS pagespeed_accessibility_score INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS pagespeed_best_practices_score INTEGER;
-- PageSpeed Audit Details (JSONB for PostgreSQL, TEXT for SQLite fallback)
-- Stores detailed audit results from PageSpeed Insights API
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS pagespeed_audits JSONB;
COMMENT ON COLUMN company_website_analysis.pagespeed_seo_score IS 'Google PageSpeed SEO score 0-100';
COMMENT ON COLUMN company_website_analysis.pagespeed_performance_score IS 'Google PageSpeed Performance score 0-100';
COMMENT ON COLUMN company_website_analysis.pagespeed_accessibility_score IS 'Google PageSpeed Accessibility score 0-100';
COMMENT ON COLUMN company_website_analysis.pagespeed_best_practices_score IS 'Google PageSpeed Best Practices score 0-100';
COMMENT ON COLUMN company_website_analysis.pagespeed_audits IS 'Full PageSpeed audit results as JSON';
-- ============================================================
-- 2. ON-PAGE SEO DETAILS
-- ============================================================
-- Note: seo_title and seo_description already exist in migration 002
-- These are additional detailed on-page SEO metrics
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS meta_title VARCHAR(500);
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS meta_description TEXT;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS meta_keywords TEXT;
-- Heading structure
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS h1_count INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS h2_count INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS h3_count INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS h1_text VARCHAR(500);
-- Image analysis
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS total_images INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS images_without_alt INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS images_with_alt INTEGER;
-- Link analysis
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS internal_links_count INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS external_links_count INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS broken_links_count INTEGER;
-- Structured data (Schema.org, JSON-LD, Microdata)
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS has_structured_data BOOLEAN DEFAULT FALSE;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS structured_data_types TEXT[];
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS structured_data_json JSONB;
COMMENT ON COLUMN company_website_analysis.h1_count IS 'Number of H1 tags on homepage (should be 1)';
COMMENT ON COLUMN company_website_analysis.h2_count IS 'Number of H2 tags on homepage';
COMMENT ON COLUMN company_website_analysis.h3_count IS 'Number of H3 tags on homepage';
COMMENT ON COLUMN company_website_analysis.h1_text IS 'Text content of first H1 tag';
COMMENT ON COLUMN company_website_analysis.images_without_alt IS 'Images missing alt attribute - accessibility issue';
COMMENT ON COLUMN company_website_analysis.has_structured_data IS 'Whether page contains JSON-LD, Microdata, or RDFa';
COMMENT ON COLUMN company_website_analysis.structured_data_types IS 'Schema.org types found: Organization, LocalBusiness, etc.';
-- ============================================================
-- 3. TECHNICAL SEO
-- ============================================================
-- Canonical URL handling
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS has_canonical BOOLEAN DEFAULT FALSE;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS canonical_url VARCHAR(500);
-- Indexability
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS is_indexable BOOLEAN DEFAULT TRUE;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS noindex_reason VARCHAR(200);
-- Mobile & Core Web Vitals
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS is_mobile_friendly BOOLEAN;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS viewport_configured BOOLEAN;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS largest_contentful_paint_ms INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS first_input_delay_ms INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS cumulative_layout_shift NUMERIC(5, 3);
-- Open Graph & Social Meta
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS has_og_tags BOOLEAN DEFAULT FALSE;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS og_title VARCHAR(500);
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS og_description TEXT;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS og_image VARCHAR(500);
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS has_twitter_cards BOOLEAN DEFAULT FALSE;
-- Language & International
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS html_lang VARCHAR(10);
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS has_hreflang BOOLEAN DEFAULT FALSE;
COMMENT ON COLUMN company_website_analysis.has_canonical IS 'Whether page has canonical URL defined';
COMMENT ON COLUMN company_website_analysis.is_indexable IS 'Whether page can be indexed (no noindex directive)';
COMMENT ON COLUMN company_website_analysis.noindex_reason IS 'Reason if page is not indexable: meta tag, robots.txt, etc.';
COMMENT ON COLUMN company_website_analysis.largest_contentful_paint_ms IS 'Core Web Vital: LCP in milliseconds';
COMMENT ON COLUMN company_website_analysis.first_input_delay_ms IS 'Core Web Vital: FID in milliseconds';
COMMENT ON COLUMN company_website_analysis.cumulative_layout_shift IS 'Core Web Vital: CLS score';
-- ============================================================
-- 4. SEO AUDIT METADATA
-- ============================================================
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS seo_audit_version VARCHAR(20);
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS seo_audited_at TIMESTAMP;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS seo_audit_errors TEXT[];
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS seo_overall_score INTEGER;
-- Custom SEO health score (calculated from all metrics)
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS seo_health_score INTEGER;
ALTER TABLE company_website_analysis ADD COLUMN IF NOT EXISTS seo_issues JSONB;
COMMENT ON COLUMN company_website_analysis.seo_audit_version IS 'Version of SEO audit script used';
COMMENT ON COLUMN company_website_analysis.seo_audited_at IS 'Timestamp of last SEO audit';
COMMENT ON COLUMN company_website_analysis.seo_audit_errors IS 'Errors encountered during SEO audit';
COMMENT ON COLUMN company_website_analysis.seo_overall_score IS 'Calculated overall SEO score 0-100';
COMMENT ON COLUMN company_website_analysis.seo_health_score IS 'On-page SEO health score 0-100';
COMMENT ON COLUMN company_website_analysis.seo_issues IS 'List of SEO issues found with severity levels';
-- ============================================================
-- 5. INDEXES FOR SEO QUERIES
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_website_seo_score ON company_website_analysis(pagespeed_seo_score);
CREATE INDEX IF NOT EXISTS idx_website_seo_audited ON company_website_analysis(seo_audited_at);
CREATE INDEX IF NOT EXISTS idx_website_seo_overall ON company_website_analysis(seo_overall_score);
-- ============================================================
-- 6. SEO DASHBOARD VIEW
-- ============================================================
CREATE OR REPLACE VIEW v_company_seo_overview AS
SELECT
c.id,
c.name,
c.slug,
c.website,
cat.name as category_name,
wa.pagespeed_seo_score,
wa.pagespeed_performance_score,
wa.pagespeed_accessibility_score,
wa.pagespeed_best_practices_score,
wa.seo_overall_score,
wa.seo_health_score,
wa.h1_count,
wa.images_without_alt,
wa.has_structured_data,
wa.has_ssl,
wa.has_sitemap,
wa.has_robots_txt,
wa.is_indexable,
wa.is_mobile_friendly,
wa.seo_audited_at,
wa.analyzed_at
FROM companies c
LEFT JOIN company_website_analysis wa ON c.id = wa.company_id
LEFT JOIN categories cat ON c.category_id = cat.id
WHERE c.website IS NOT NULL AND c.website != ''
ORDER BY wa.seo_overall_score DESC NULLS LAST;
COMMENT ON VIEW v_company_seo_overview IS 'SEO metrics overview for admin dashboard';
-- ============================================================
-- MIGRATION COMPLETE
-- ============================================================
-- Verify migration (PostgreSQL only)
DO $$
BEGIN
RAISE NOTICE 'Migration 004 completed successfully!';
RAISE NOTICE 'Added columns to company_website_analysis:';
RAISE NOTICE ' - PageSpeed scores (seo, performance, accessibility, best_practices)';
RAISE NOTICE ' - On-page SEO details (headings, images, links, structured data)';
RAISE NOTICE ' - Technical SEO (canonical, indexability, Core Web Vitals)';
RAISE NOTICE ' - SEO audit metadata (version, timestamp, scores)';
RAISE NOTICE 'Created view:';
RAISE NOTICE ' - v_company_seo_overview';
END $$;