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>
This commit is contained in:
parent
4b2a68af74
commit
8f5bb9a47c
190
database/migrations/004_seo_metrics.sql
Normal file
190
database/migrations/004_seo_metrics.sql
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 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 $$;
|
||||||
Loading…
Reference in New Issue
Block a user