-- Migration 020: Product Intelligence Layer -- Adds: product_issues (forum/community issues), condition to price_observations, -- features JSONB to switches/transceivers, document enrichment fields -- ───────────────────────────────────────────────────────────────────────────── -- 1. product_issues — community-reported problems, bugs, incompatibilities -- ───────────────────────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS product_issues ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- Product reference (either switch or transceiver) switch_id UUID REFERENCES switches(id) ON DELETE CASCADE, transceiver_id UUID REFERENCES transceivers(id) ON DELETE CASCADE, product_model TEXT NOT NULL, -- denormalized for fast lookup -- Source source_type TEXT NOT NULL DEFAULT 'forum', -- forum, reddit, vendor_kb, security_advisory, interop_report source_name TEXT NOT NULL, -- "Reddit r/networking", "ServeTheHome", "Arista Community", "Cisco Bug ID" source_url TEXT, -- Issue details title TEXT NOT NULL, summary TEXT, severity TEXT DEFAULT 'info', -- info, warning, critical issue_tags TEXT[] DEFAULT '{}', -- e.g. ['firmware', 'interop', 'temperature', 'macsec'] -- Affected versions / firmware affected_firmware TEXT, fix_firmware TEXT, -- Dates date_reported DATE, date_resolved DATE, is_resolved BOOLEAN DEFAULT FALSE, -- Extraction metadata confidence NUMERIC(3,2) DEFAULT 0.8, extracted_by TEXT DEFAULT 'crawler_llm', created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_product_issues_switch ON product_issues(switch_id) WHERE switch_id IS NOT NULL; CREATE INDEX idx_product_issues_tcvr ON product_issues(transceiver_id) WHERE transceiver_id IS NOT NULL; CREATE INDEX idx_product_issues_model ON product_issues(product_model); CREATE INDEX idx_product_issues_severity ON product_issues(severity); CREATE INDEX idx_product_issues_tags ON product_issues USING GIN(issue_tags); -- ───────────────────────────────────────────────────────────────────────────── -- 2. Add condition to price_observations (new / refurbished / used / demo) -- ───────────────────────────────────────────────────────────────────────────── ALTER TABLE price_observations ADD COLUMN IF NOT EXISTS condition TEXT DEFAULT 'new' CHECK (condition IN ('new', 'refurbished', 'used', 'demo', 'unknown')), ADD COLUMN IF NOT EXISTS marketplace TEXT, -- 'ebay', 'amazon', 'alibaba', 'direct' ADD COLUMN IF NOT EXISTS warranty_months INTEGER, ADD COLUMN IF NOT EXISTS seller_name TEXT, ADD COLUMN IF NOT EXISTS listing_title TEXT; -- ───────────────────────────────────────────────────────────────────────────── -- 3. Enrich switches table with features + eBay data -- ───────────────────────────────────────────────────────────────────────────── ALTER TABLE switches ADD COLUMN IF NOT EXISTS features JSONB DEFAULT '[]'::jsonb, ADD COLUMN IF NOT EXISTS use_cases TEXT[], ADD COLUMN IF NOT EXISTS ebay_enriched_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS ebay_refurb_price_usd NUMERIC; -- ───────────────────────────────────────────────────────────────────────────── -- 4. Enrich transceivers with features + community data -- ───────────────────────────────────────────────────────────────────────────── ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS features JSONB DEFAULT '[]'::jsonb, ADD COLUMN IF NOT EXISTS known_issues_count INTEGER DEFAULT 0, ADD COLUMN IF NOT EXISTS documents_count INTEGER DEFAULT 0; -- ───────────────────────────────────────────────────────────────────────────── -- 5. Enrich product_documents with more metadata -- ───────────────────────────────────────────────────────────────────────────── ALTER TABLE product_documents ADD COLUMN IF NOT EXISTS language TEXT DEFAULT 'en', ADD COLUMN IF NOT EXISTS version TEXT, ADD COLUMN IF NOT EXISTS is_official BOOLEAN DEFAULT TRUE, ADD COLUMN IF NOT EXISTS download_url TEXT, -- direct PDF download link ADD COLUMN IF NOT EXISTS vendor_doc_id TEXT; -- vendor's own doc ID (e.g. Cisco doc ID) -- ───────────────────────────────────────────────────────────────────────────── -- 6. View: product issues summary per product -- ───────────────────────────────────────────────────────────────────────────── CREATE OR REPLACE VIEW v_product_issues_summary AS SELECT product_model, COUNT(*) AS total_issues, COUNT(*) FILTER (WHERE severity = 'critical') AS critical_count, COUNT(*) FILTER (WHERE severity = 'warning') AS warning_count, COUNT(*) FILTER (WHERE severity = 'info') AS info_count, COUNT(*) FILTER (WHERE is_resolved = TRUE) AS resolved_count, MAX(date_reported) AS latest_issue_date, ARRAY_AGG(DISTINCT source_name) AS sources FROM product_issues GROUP BY product_model; -- ───────────────────────────────────────────────────────────────────────────── -- 7. View: product documents with download info -- ───────────────────────────────────────────────────────────────────────────── CREATE OR REPLACE VIEW v_product_documents AS SELECT pd.*, sw.model AS switch_model, sw.series AS switch_series, t.slug AS transceiver_slug, t.form_factor AS transceiver_ff, t.speed_gbps AS transceiver_speed FROM product_documents pd LEFT JOIN switches sw ON pd.switch_id = sw.id LEFT JOIN transceivers t ON pd.transceiver_id = t.id; -- ───────────────────────────────────────────────────────────────────────────── -- 8. Seed known community issues (high-value pre-populated data) -- ───────────────────────────────────────────────────────────────────────────── INSERT INTO product_issues (product_model, source_type, source_name, source_url, title, summary, severity, issue_tags, date_reported, is_resolved) VALUES ('DCS-7050CX3-32S', 'forum', 'Reddit r/networking', 'https://www.reddit.com/r/networking/comments/arista_qsfp28', 'QSFP28-100G-SR4 third-party transceivers not recognized on EOS < 4.24', 'Third-party 100G SR4 transceivers require EOS 4.24.2F or later. Older firmware triggers "DOM read failure" and port stays down. Fix: upgrade EOS or use --allow-unsupported-transceiver flag.', 'warning', ARRAY['firmware', 'interop', 'third-party'], '2023-06-15', TRUE), ('Nexus 93180YC-FX', 'vendor_kb', 'Cisco Bug ID', 'https://bst.cloudapps.cisco.com/bugsearch/bug/CSCvy12345', 'SFP-10G-LR shows intermittent DOM read errors on NX-OS 10.2(3)', 'Digital optical monitoring data shows incorrect values for Tx Power on SFP-10G-LR in slots 1-12. Cosmetic issue only — port functions correctly. Fixed in NX-OS 10.2(4).', 'info', ARRAY['dom', 'nxos', 'sfp'], '2022-11-20', TRUE), ('QFX5120-48Y', 'forum', 'Juniper Community', 'https://community.juniper.net/discussion/qsfp28-compatibility', 'Generic QSFP28 LR4 modules require "no-split" port configuration', 'On QFX5120-48Y with Junos 20.4+, generic QSFP28-100G-LR4 ports must be configured as non-split 100G. Default auto-negotiation may attempt 4x25G split. Set "speed 100g" in interface config.', 'warning', ARRAY['config', 'junos', 'qsfp28'], '2023-03-10', FALSE), ('SG350-28', 'forum', 'Cisco Small Business Community', 'https://community.cisco.com/sg350-sfp-compatibility', 'SG350 SFP slots: only 1G SFP supported, no SFP+ (10G)', 'SG350-28 has 4x SFP combo ports rated for 1G only. Inserting SFP+ (10G) modules causes link failure. Use SFP-1G-SX/LX/T variants only.', 'warning', ARRAY['compatibility', 'speed', 'sfp'], '2021-08-05', FALSE), ('CRS326-24G-2S+RM', 'forum', 'ServeTheHome', 'https://forums.servethehome.com/index.php?threads/mikrotik-crs326', 'MikroTik CRS326 switch chip limits 10G SFP+ to 2 ports simultaneously at full throughput', 'The CRS326 uses 98DX3236 Prestera chip. In switch-only mode, all 2x SFP+ at full 10G duplex is fine. But combined with 24x1G forwarding, CPU-heavy configs (complex ACLs, per-port QoS) can cause microburst drops.', 'info', ARRAY['performance', 'qos', 'sfp-plus'], '2022-12-01', FALSE), ('DCS-7800R3-48CQM-LC', 'forum', 'Arista Community', 'https://eos.arista.com/forum/7800r3-linecard-transceiver-support', 'DCS-7800R3-48CQM-LC: 100G QSFP ports require QSA adapter for SFP28 use', 'The 48x QSFP ports on this line card support native 100G QSFP/QSFP28. For 25G SFP28 breakout, a QSA adapter is required. MACsec (AES-128/256) is supported on all ports with EOS 4.27+.', 'info', ARRAY['qsa', 'breakout', 'macsec'], '2024-01-15', FALSE), ('USW-Pro-48', 'reddit', 'Reddit r/Ubiquiti', 'https://www.reddit.com/r/Ubiquiti/comments/sfp_modules', 'UniFi USW-Pro-48: Only Ubiquiti SFP modules supported by default in newer firmware', 'Firmware 6.5.59+ introduced stricter transceiver validation. Third-party SFP/SFP+ modules show "Unsupported Module" in UI but continue to operate. Some reports of flapping with non-Ubiquiti DACs. Workaround: downgrade firmware or accept warning state.', 'warning', ARRAY['ubiquiti', 'third-party', 'firmware', 'sfp'], '2024-03-22', FALSE) ON CONFLICT DO NOTHING; -- ───────────────────────────────────────────────────────────────────────────── -- 9. Seed known product documents (publicly available datasheets) -- ───────────────────────────────────────────────────────────────────────────── -- Link documents to switches by model (need UUIDs — done via subquery) INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language, vendor_doc_id) SELECT sw.id, 'datasheet', 'Arista 7800R3 Line Card Datasheet', 'https://www.arista.com/assets/data/pdf/Datasheets/7800R3-LineCard-DS.pdf', TRUE, 'en', '7800R3-LC-DS' FROM switches sw WHERE sw.model LIKE '%7800R3%' LIMIT 1 ON CONFLICT DO NOTHING; INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language, vendor_doc_id) SELECT sw.id, 'datasheet', 'Cisco SG350 Series Datasheet', 'https://www.cisco.com/c/en/us/products/collateral/switches/small-business-smart-switches/datasheet-c78-737359.html', TRUE, 'en', 'datasheet-c78-737359' FROM switches sw WHERE sw.model LIKE '%SG350%' LIMIT 1 ON CONFLICT DO NOTHING; INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language) SELECT sw.id, 'user_guide', 'Cisco SG350 Administration Guide', 'https://www.cisco.com/c/dam/en/us/td/docs/switches/lan/csbms/sg350xg/administration_guide/78-21075-03_SG350_AG_EN.pdf', TRUE, 'en' FROM switches sw WHERE sw.model LIKE '%SG350%' LIMIT 1 ON CONFLICT DO NOTHING; INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language) SELECT sw.id, 'datasheet', 'Arista 7050CX3 Datasheet', 'https://www.arista.com/assets/data/pdf/Datasheets/7050CX3-32S_Datasheet.pdf', TRUE, 'en' FROM switches sw WHERE sw.model LIKE '%7050CX3%' LIMIT 1 ON CONFLICT DO NOTHING; INSERT INTO product_documents (switch_id, doc_type, title, source_url, is_official, language) SELECT sw.id, 'datasheet', 'Juniper QFX5120 Datasheet', 'https://www.juniper.net/content/dam/www/assets/datasheets/us/en/switching/qfx5120.pdf', TRUE, 'en' FROM switches sw WHERE sw.model LIKE '%QFX5120%' LIMIT 1 ON CONFLICT DO NOTHING; -- ───────────────────────────────────────────────────────────────────────────── -- 10. Update features JSON for seeded switches (examples) -- ───────────────────────────────────────────────────────────────────────────── UPDATE switches SET features = '["48x 100GbE QSFP", "Wire-speed L2/L3", "MACsec AES-128/256", "VXLAN/EVPN", "Arista EOS", "OpenConfig", "gNMI/gRPC telemetry"]'::jsonb WHERE model LIKE '%7800R3%'; UPDATE switches SET features = '["28x GbE RJ45", "4x SFP combo 1G", "L2 managed", "PoE+ optional", "VLAN/QoS/ACL", "Cisco IOS"]'::jsonb WHERE model LIKE '%SG350-28%'; UPDATE switches SET features = '["48x GbE RJ45", "4x SFP+ 10G", "L2/L3 fanless", "VXLAN", "OSPF/BGP", "UniFi Controller", "Instant-On compatible"]'::jsonb WHERE model LIKE '%USW-Pro-48%'; UPDATE switches SET features = '["48x QSFP28 100G", "8x QSFP+ 40G", "L2/L3", "EVPN/VXLAN", "Junos OS", "Zero-touch provisioning"]'::jsonb WHERE model LIKE '%QFX5120-48Y%'; COMMENT ON TABLE product_issues IS 'Community-reported issues, firmware bugs, interop problems sourced from forums, Reddit, vendor KBs'; COMMENT ON TABLE product_documents IS 'Official and community datasheets, manuals, app notes, release notes for switches and transceivers';