From ba998f4c0114926531ad8eccf8b25a4b82b8e8a3 Mon Sep 17 00:00:00 2001 From: Rene Fichtmueller Date: Sat, 25 Apr 2026 08:55:21 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20vendor=5Fcompat=200%=E2=86=92100%,=20pri?= =?UTF-8?q?ce=20denorm,=20wiitek=20disabled,=20price-denorm=20scheduler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Migration 094: images for 12 Cisco 8K MPA + A9K-8HG-FLEX + ASR-9000V models - Migration 095: price denorm refresh (EUR 679→1376, USD 166→835 with 180d window) - Migration 096: bulk vendor_compat by form_factor — all 9013 transceivers now have OEM compatibility patterns (was 0/9013 because all slugs are scraped-*) - wiitek.ts: disable dead scraper (wiitek.com unreachable since 2026-04, EAI_AGAIN) - scheduler.ts: add compute:price-denorm job (daily 05:30 UTC) to keep street_price_usd/price_verified_eur fresh without manual migration runs - seed-from-npm.ts: ON CONFLICT now also updates vendor_compat (was only updated_at) --- packages/scraper/src/scheduler.ts | 38 ++- packages/scraper/src/scrapers/wiitek.ts | 79 +----- scripts/seed-from-npm.ts | 4 +- sql/094-cisco-8000-mpa-a9k-flex-images.sql | 109 ++++++++ sql/095-vendor-compat-and-price-denorm.sql | 137 ++++++++++ sql/096-vendor-compat-by-form-factor.sql | 277 +++++++++++++++++++++ 6 files changed, 564 insertions(+), 80 deletions(-) create mode 100644 sql/094-cisco-8000-mpa-a9k-flex-images.sql create mode 100644 sql/095-vendor-compat-and-price-denorm.sql create mode 100644 sql/096-vendor-compat-by-form-factor.sql diff --git a/packages/scraper/src/scheduler.ts b/packages/scraper/src/scheduler.ts index 1b85c83..2411b84 100644 --- a/packages/scraper/src/scheduler.ts +++ b/packages/scraper/src/scheduler.ts @@ -147,6 +147,8 @@ export async function registerSchedules(boss: PgBoss): Promise { "sync:nas", // ── Health Monitoring ───────────────────────────────────────────── "monitor:scraper-health", + // ── Price denormalization refresh ───────────────────────────────── + "compute:price-denorm", // ── Verification Reconciliation ─────────────────────────────────── "maintenance:reconcile-verification", // ── Competitor Equivalence Matching ─────────────────────────────── @@ -273,6 +275,8 @@ export async function registerSchedules(boss: PgBoss): Promise { await boss.schedule("compute:abc", "50 3,7,11,15,19,23 * * *", {}, { retryLimit: 2, expireInSeconds: 600 }); await boss.schedule("compute:reorder-signals", "55 3,7,11,15,19,23 * * *", {}, { retryLimit: 2, expireInSeconds: 600 }); + // Price denorm refresh: daily 05:30 UTC after overnight scraping waves settle + await boss.schedule("compute:price-denorm", "30 5 * * *", {}, { retryLimit: 1, expireInSeconds: 600 }); // ══════════════════════════════════════════════════════════════════════ // PREDICTION SIGNAL SCRAPERS @@ -319,7 +323,7 @@ export async function registerSchedules(boss: PgBoss): Promise { // Re-research approved equivalences: daily at 03:00 UTC, processes 200 items per run await boss.schedule("maintenance:re-research-equivalences", "0 3 * * *", {}, { retryLimit: 1, expireInSeconds: 3600 }); - console.log("All schedules registered — 24/7 continuous scraping (59 jobs)"); + console.log("All schedules registered — 24/7 continuous scraping (60 jobs)"); } export async function registerWorkers(boss: PgBoss): Promise { @@ -753,6 +757,36 @@ export async function registerWorkers(boss: PgBoss): Promise { await scrapeMouser(); }); + // ── Price denormalization refresh ───────────────────────────────────────── + // Refreshes street_price_usd / price_verified_eur on the transceivers table from + // the price_observations hypertable. Without this, denormalized prices go stale + // even when scrapers are collecting new observations. + await boss.work("compute:price-denorm", async () => { + const { pool } = await import("./utils/db"); + const ts = new Date().toISOString(); + console.log(`[${ts}] Running: Price denormalization refresh`); + + const result = await pool.query(` + UPDATE transceivers t + SET price_verified_eur = sub.price_eur, + street_price_usd = sub.price_usd, + updated_at = NOW() + FROM ( + SELECT po.transceiver_id, + MAX(po.price) FILTER (WHERE po.currency = 'EUR') AS price_eur, + MAX(po.price) FILTER (WHERE po.currency = 'USD') AS price_usd + FROM price_observations po + WHERE po.time > NOW() - INTERVAL '180 days' + AND po.price > 0 + GROUP BY po.transceiver_id + ) sub + WHERE t.id = sub.transceiver_id + AND (sub.price_eur IS NOT NULL OR sub.price_usd IS NOT NULL) + `); + + console.log(`[price-denorm] refreshed ${result.rowCount} transceivers`); + }); + // ── Health monitor ────────────────────────────────────────────────────── await boss.work("monitor:scraper-health", async () => { const { pool } = await import("./utils/db"); @@ -1174,5 +1208,5 @@ export async function registerWorkers(boss: PgBoss): Promise { console.log(`[re-research] confirmed: ${confirmed}, reverted to pending: ${reverted}, batch size: ${batch.rows.length}`); }); - console.log("All workers registered (78 jobs, 24/7 continuous)"); + console.log("All workers registered (79 jobs, 24/7 continuous)"); } diff --git a/packages/scraper/src/scrapers/wiitek.ts b/packages/scraper/src/scrapers/wiitek.ts index 1eabe61..0fa2d98 100644 --- a/packages/scraper/src/scrapers/wiitek.ts +++ b/packages/scraper/src/scrapers/wiitek.ts @@ -7,83 +7,8 @@ * * Schedule: every 8h */ -import * as cheerio from "cheerio"; -import { ensureVendor, upsertPriceObservation, findOrCreateScrapedTransceiver } from "../utils/db"; -import { contentHash, parsePrice } from "../utils/hash"; -import { logger } from "../utils/logger"; - -const BASE = "https://www.wiitek.com"; - -const CATEGORIES: Array<{ path: string; form_factor: string }> = [ - { path: "/SFP-Transceiver/", form_factor: "SFP" }, - { path: "/SFP-Plus-Transceiver/", form_factor: "SFP+" }, - { path: "/SFP28-Transceiver/", form_factor: "SFP28" }, - { path: "/SFP56-Transceiver/", form_factor: "SFP56" }, - { path: "/SFP-DD-Transceiver/", form_factor: "SFP-DD" }, - { path: "/CSFP-Transceiver/", form_factor: "CSFP" }, - { path: "/QSFP-Transceiver/", form_factor: "QSFP+" }, - { path: "/QSFP28-Transceiver/", form_factor: "QSFP28" }, - { path: "/QSFP56-Transceiver/", form_factor: "QSFP56" }, - { path: "/QSFP-DD-Transceiver/", form_factor: "QSFP-DD" }, - { path: "/QSFP-DD800-Transceiver/", form_factor: "QSFP-DD800" }, - { path: "/QSFP112-Transceiver/", form_factor: "QSFP112" }, - { path: "/OSFP-Transceiver/", form_factor: "OSFP" }, - { path: "/OSFP112-Transceiver/", form_factor: "OSFP112" }, - { path: "/OSFP224-Transceiver/", form_factor: "OSFP224" }, - { path: "/CFP-Transceiver/", form_factor: "CFP" }, - { path: "/CFP2-Transceiver/", form_factor: "CFP2" }, - { path: "/XFP-Transceiver/", form_factor: "XFP" }, - { path: "/GBIC-Transceiver/", form_factor: "GBIC" }, - { path: "/XENPAK-Transceiver/", form_factor: "XENPAK" }, - { path: "/CXP-Transceiver/", form_factor: "CXP" }, -]; export async function scrapeWiitek(): Promise { - logger.info("Wiitek scraper starting"); - const vendorId = await ensureVendor("Wiitek", BASE); - let total = 0; - let newItems = 0; - - for (const cat of CATEGORIES) { - try { - const resp = await fetch(`${BASE}${cat.path}`, { - headers: { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" }, - signal: AbortSignal.timeout(20_000), - }); - if (!resp.ok) continue; - const $ = cheerio.load(await resp.text()); - - const items = $(".product-item, .goods-item, .pro-item, [class*=product]"); - for (let i = 0; i < items.length; i++) { - const $el = $(items[i]); - const name = $el.find("h2,h3,h4,.title,.name").first().text().trim(); - const priceText = $el.find(".price,.cost,[class*=price]").first().text().trim(); - const href = $el.find("a[href]").first().attr("href") || ""; - if (!name || !priceText) continue; - - const partMatch = name.match(/([A-Z0-9]{2,8}[-\/][A-Z0-9][A-Z0-9\-\.\/]{3,35})/); - const partNumber = (partMatch ? partMatch[1] : name.substring(0, 50)).toUpperCase(); - const { price, currency } = parsePrice(priceText); - if (price <= 0) continue; - - try { - const transceiverId = await findOrCreateScrapedTransceiver({ - partNumber, vendorId, formFactor: cat.form_factor, - }); - const isNew = await upsertPriceObservation({ - transceiverId, sourceVendorId: vendorId, - price, currency: currency || "USD", - stockLevel: "unknown", - url: href.startsWith("http") ? href : `${BASE}${href}`, - contentHash: contentHash({ partNumber, price, currency: currency || "USD" }), - }); - if (isNew) newItems++; - total++; - } catch { /* skip */ } - } - } catch (e) { - logger.warn(`Wiitek ${cat.form_factor} failed`, { err: e }); - } - } - logger.info(`Wiitek done — ${total} total, ${newItems} new`); + // wiitek.com unreachable since 2026-04 (ConnectTimeoutError on all requests) + console.warn("[wiitek] Scraper disabled — www.wiitek.com:443 connection timeout on every attempt (2026-04-25)"); } diff --git a/scripts/seed-from-npm.ts b/scripts/seed-from-npm.ts index 527cbdb..77cbda6 100644 --- a/scripts/seed-from-npm.ts +++ b/scripts/seed-from-npm.ts @@ -181,7 +181,9 @@ async function seedTransceivers( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29 - ) ON CONFLICT (slug) DO UPDATE SET updated_at = NOW()`, + ) ON CONFLICT (slug) DO UPDATE SET + vendor_compat = EXCLUDED.vendor_compat, + updated_at = NOW()`, [ t.id, t.standard, diff --git a/sql/094-cisco-8000-mpa-a9k-flex-images.sql b/sql/094-cisco-8000-mpa-a9k-flex-images.sql new file mode 100644 index 0000000..aa90d09 --- /dev/null +++ b/sql/094-cisco-8000-mpa-a9k-flex-images.sql @@ -0,0 +1,109 @@ +-- Migration 094: Cisco 8K-MPA series + A9K-8HG-FLEX + misc image backfill +-- Models: 8K-MPA-4D, 8K-MPA-16H, 8K-MPA-16Z2D, A9K-8HG-FLEX-FC, A9K-8HG-FLEX-SE, +-- A9K-8HG-FLEX-TR, A9K-400G-DWDM-TR, N9348Y12C-SE1, NC55-36X100G-A-SE, +-- ASR-9000V-24-A, ASR-9000V-DC-E, ASR-9922-RP-TR +-- Sources: manuals.plus (Cisco 8700 HIG figures), networkgenetics.net (BigCommerce), +-- teksavers.com, cisco.com/c/dam, inteleca.com (BigCommerce), +-- stack-systems.com (Magento CDN), it-market.com +-- Coverage: +12 models (~650 → 662 estimated) +-- Note: 8K-MPA-18Z1D skipped — product too new, no accessible reseller/CDN image found. +-- ASR-9000V-24-A uses ASR-9000V-AC image (bundle SKU, identical chassis). +-- A9K-8HG-FLEX-FC uses TR image (FC/SE/TR are identical hardware, license-only difference). +-- NC55-36X100G-A-SE uses -BA variant image (same physical PCB, different optics license). + +-- 8K-MPA-4D: manuals.plus Cisco 8700 HIG Figure 4 (88KB PNG, unique PCB photo) +UPDATE switches +SET image_url = 'https://manuals.plus/wp-content/uploads/2022/12/FIG-4-cisco-8700-modular-port-adapters-hardware-installation-guide.png', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/8000-series-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = '8K-MPA-4D' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- 8K-MPA-16H: manuals.plus Cisco 8700 HIG Figure 7 (91KB PNG, unique PCB photo) +UPDATE switches +SET image_url = 'https://manuals.plus/wp-content/uploads/2022/12/FIG-7-cisco-8700-modular-port-adapters-hardware-installation-guide.png', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/8000-series-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = '8K-MPA-16H' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- 8K-MPA-16Z2D: manuals.plus Cisco 8700 HIG Figure 10 (89KB PNG, unique PCB photo) +UPDATE switches +SET image_url = 'https://manuals.plus/wp-content/uploads/2022/12/FIG-10-cisco-8700-modular-port-adapters-hardware-installation-guide.png', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/8000-series-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = '8K-MPA-16Z2D' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- A9K-8HG-FLEX-FC: networkgenetics.net BigCommerce CDN (321KB PNG, product-specific front photo) +-- FC/SE/TR are identical hardware units; only the software license differs +UPDATE switches +SET image_url = 'https://cdn11.bigcommerce.com/s-e692hdujm7/images/stencil/1280x1280/products/9599/13077/Screen%5FShot%5F2024-05-16%5Fat%5F12.10.29%5FPM%5F%5F78724.1715879628.png?c=2', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/asr-9000-series-aggregation-services-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = 'A9K-8HG-FLEX-FC' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- A9K-8HG-FLEX-SE: networkgenetics.net BigCommerce CDN (321KB PNG, same hardware as FC/TR) +UPDATE switches +SET image_url = 'https://cdn11.bigcommerce.com/s-e692hdujm7/images/stencil/1280x1280/products/9599/13077/Screen%5FShot%5F2024-05-16%5Fat%5F12.10.29%5FPM%5F%5F78724.1715879628.png?c=2', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/asr-9000-series-aggregation-services-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = 'A9K-8HG-FLEX-SE' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- A9K-8HG-FLEX-TR: networkgenetics.net BigCommerce CDN (321KB PNG, product-specific front photo) +UPDATE switches +SET image_url = 'https://cdn11.bigcommerce.com/s-e692hdujm7/images/stencil/1280x1280/products/9599/13077/Screen%5FShot%5F2024-05-16%5Fat%5F12.10.29%5FPM%5F%5F78724.1715879628.png?c=2', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/asr-9000-series-aggregation-services-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = 'A9K-8HG-FLEX-TR' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- A9K-400G-DWDM-TR: teksavers.com product photo (78KB PNG, verified) +UPDATE switches +SET image_url = 'https://www.teksavers.com/media/catalog/product/cache/a99f2b4b01cf8c5c2cfc3d0e49e4e12a/a/9/a9k-400g-dwdm-tr.png', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/asr-9000-series-aggregation-services-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = 'A9K-400G-DWDM-TR' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- N9348Y12C-SE1: Cisco official DAM image (145KB PNG, N9300 SE1 series datasheet photo) +UPDATE switches +SET image_url = 'https://www.cisco.com/c/dam/en/us/products/collateral/switches/nexus-9000-series-switches/nb-06-nexus-93-se1-aag-cte-en.docx/_jcr_content/renditions/nb-06-nexus-93-se1-aag-cte-en_0.png', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/switches/nexus-9000-series-switches/index.html'), + assets_scraped_at = NOW() +WHERE model = 'N9348Y12C-SE1' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- NC55-36X100G-A-SE: inteleca.com BigCommerce CDN (69KB JPEG, -BA variant same PCB) +UPDATE switches +SET image_url = 'https://cdn11.bigcommerce.com/s-4yguupzb2p/images/stencil/1280x1280/products/NC55-36X100G-BA-SE/main/NC55-36X100G-BA-SE__01234.jpg?c=2', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/network-convergence-system-5500-series/index.html'), + assets_scraped_at = NOW() +WHERE model = 'NC55-36X100G-A-SE' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- ASR-9000V-24-A: stack-systems.com Magento CDN (ASR-9000V-AC chassis, bundle SKU = same hardware) +UPDATE switches +SET image_url = 'https://stack-systems.com/media/catalog/product/cache/4ccf3122b2d4a5eb47d88ec1c5d09b3d/a/s/asr-9000v-ac.jpg', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/asr-9000-series-aggregation-services-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = 'ASR-9000V-24-A' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- ASR-9000V-DC-E: it-market.com product photo (verified) +UPDATE switches +SET image_url = 'https://it-market.com/media/20/12/05/1702071778/asr-9000v-dc-e.jpg', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/asr-9000-series-aggregation-services-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = 'ASR-9000V-DC-E' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); + +-- ASR-9922-RP-TR: networkgenetics.net BigCommerce CDN (verified JPEG) +UPDATE switches +SET image_url = 'https://cdn11.bigcommerce.com/s-e692hdujm7/images/stencil/1280x1280/products/9622/13118/ASR-9922-RP-TR__78456.1715880012.jpg?c=2', + product_page_url = COALESCE(product_page_url, 'https://www.cisco.com/c/en/us/products/routers/asr-9000-series-aggregation-services-routers/index.html'), + assets_scraped_at = NOW() +WHERE model = 'ASR-9922-RP-TR' + AND vendor_id = (SELECT id FROM vendors WHERE slug = 'cisco'); diff --git a/sql/095-vendor-compat-and-price-denorm.sql b/sql/095-vendor-compat-and-price-denorm.sql new file mode 100644 index 0000000..f9b3d0a --- /dev/null +++ b/sql/095-vendor-compat-and-price-denorm.sql @@ -0,0 +1,137 @@ +-- Migration 095: Vendor compat backfill + price denormalization refresh +-- Problem 1: vendor_compat = '[]' for ALL 9013 transceivers (never populated by any scraper) +-- Problem 2: street_price_usd = 166/9013, price_verified_eur = 679/9013 (compute fn never scheduled) +-- Fix 1: Bulk UPDATE vendor_compat for 159 core npm-package transceivers via slug matching +-- Fix 2: Run compute_transceiver_verification() + add 24h scheduled refresh +-- Applied: 2026-04-25 + +-- ── 1. Vendor compat bulk update (159 core transceivers) ───────────────────── + +UPDATE transceivers AS t +SET vendor_compat = v.vc::jsonb, + updated_at = NOW() +FROM (VALUES + ('gbic-sx', '[{"vendor":"Cisco","partPattern":"WS-G5484"},{"vendor":"Juniper","partPattern":"SRX-SFP-1GE-SX"}]'), + ('gbic-lx', '[{"vendor":"Cisco","partPattern":"WS-G5486"}]'), + ('sfp-sx', '[{"vendor":"Cisco","partPattern":"GLC-SX-MMD"},{"vendor":"Juniper","partPattern":"EX-SFP-1GE-SX"},{"vendor":"Arista","partPattern":"SFP-1G-SX"},{"vendor":"Huawei","partPattern":"SFP-GE-SX"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp-lx', '[{"vendor":"Cisco","partPattern":"GLC-LH-SMD"},{"vendor":"Juniper","partPattern":"EX-SFP-1GE-LX"},{"vendor":"Arista","partPattern":"SFP-1G-LX"},{"vendor":"Huawei","partPattern":"SFP-GE-LX-SM1310"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp-zx', '[{"vendor":"Cisco","partPattern":"GLC-ZX-SMD"},{"vendor":"Juniper","partPattern":"SFP-1GE-LH"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('sfp-t', '[{"vendor":"Cisco","partPattern":"GLC-T"},{"vendor":"Juniper","partPattern":"EX-SFP-1GE-T"},{"vendor":"Arista","partPattern":"SFP-1G-T"},{"vendor":"Huawei","partPattern":"SFP-GE-T"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp-bidi-1310-1550','[{"vendor":"Cisco","partPattern":"GLC-BX-U"},{"vendor":"Juniper","partPattern":"SFP-1GE-BX"},{"vendor":"Arista","partPattern":"SFP-1G-BXUP"},{"vendor":"Huawei","partPattern":"SFP-GE-BX"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp-cwdm-1470', '[{"vendor":"Cisco","partPattern":"CWDM-SFP-1470"},{"vendor":"Juniper","partPattern":"SFP-1GE-CWDM-1470"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('xfp-sr', '[{"vendor":"Cisco","partPattern":"XFP-10G-MM-SR"},{"vendor":"Juniper","partPattern":"XFP-10G-S"}]'), + ('xfp-lr', '[{"vendor":"Cisco","partPattern":"XFP-10GLR-OC192SR"},{"vendor":"Juniper","partPattern":"XFP-10G-L-OC192-SR1"}]'), + ('xfp-er', '[{"vendor":"Cisco","partPattern":"XFP-10GER-OC192IR"},{"vendor":"Juniper","partPattern":"XFP-10GE-ER"}]'), + ('sfpp-sr', '[{"vendor":"Cisco","partPattern":"SFP-10G-SR"},{"vendor":"Juniper","partPattern":"EX-SFP-10GE-SR"},{"vendor":"Arista","partPattern":"SFP-10G-SR"},{"vendor":"Huawei","partPattern":"SFP-10G-USR"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10301"}]'), + ('sfpp-lr', '[{"vendor":"Cisco","partPattern":"SFP-10G-LR"},{"vendor":"Juniper","partPattern":"EX-SFP-10GE-LR"},{"vendor":"Arista","partPattern":"SFP-10G-LR"},{"vendor":"Huawei","partPattern":"SFP-10G-LR"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J9151*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10302"}]'), + ('sfpp-er', '[{"vendor":"Cisco","partPattern":"SFP-10G-ER"},{"vendor":"Juniper","partPattern":"EX-SFP-10GE-ER"},{"vendor":"Arista","partPattern":"SFP-10G-ER"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('sfpp-zr', '[{"vendor":"Cisco","partPattern":"SFP-10G-ZR"},{"vendor":"Juniper","partPattern":"SFP-10GE-ZR"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('sfpp-bidi-10', '[{"vendor":"Cisco","partPattern":"SFP-10G-BXU-I"},{"vendor":"Juniper","partPattern":"SFP-10GE-BX"},{"vendor":"Arista","partPattern":"SFP-10G-BXUP"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfpp-cwdm', '[{"vendor":"Cisco","partPattern":"CWDM-SFP10G-*"},{"vendor":"Juniper","partPattern":"SFP-10GE-CWDM*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('sfpp-dwdm', '[{"vendor":"Cisco","partPattern":"DWDM-SFP10G-*"},{"vendor":"Juniper","partPattern":"SFP-10GE-DWDM*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('sfpp-t', '[{"vendor":"Cisco","partPattern":"SFP-10G-T"},{"vendor":"Juniper","partPattern":"SFP-10GE-T"},{"vendor":"Arista","partPattern":"SFP-10G-T"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfpp-usr', '[{"vendor":"Cisco","partPattern":"SFP-10G-SR"},{"vendor":"Huawei","partPattern":"SFP-10G-USR"}]'), + ('sfpp-lrm', '[{"vendor":"Cisco","partPattern":"SFP-10G-LRM"},{"vendor":"Juniper","partPattern":"EX-SFP-10GE-LRM"}]'), + ('sfpp-lr-ind', '[{"vendor":"Cisco","partPattern":"SFP-10G-LR-S"},{"vendor":"Juniper","partPattern":"EX-SFP-10GE-LR"},{"vendor":"Arista","partPattern":"SFP-10G-LR"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp-lx-ind', '[{"vendor":"Cisco","partPattern":"GLC-LH-SMD"},{"vendor":"Juniper","partPattern":"EX-SFP-1GE-LX"},{"vendor":"Arista","partPattern":"SFP-1G-LX"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp28-sr', '[{"vendor":"Cisco","partPattern":"SFP-25G-SR-S"},{"vendor":"Juniper","partPattern":"SFP-25GE-SR"},{"vendor":"Arista","partPattern":"SFP-25G-SR"},{"vendor":"Huawei","partPattern":"SFP-25G-SR"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"P*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp28-lr', '[{"vendor":"Cisco","partPattern":"SFP-25G-LR-S"},{"vendor":"Juniper","partPattern":"SFP-25GE-LR"},{"vendor":"Arista","partPattern":"SFP-25G-LR"},{"vendor":"Huawei","partPattern":"SFP-25G-LR"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"P*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp28-er', '[{"vendor":"Cisco","partPattern":"SFP-25G-ER-S"},{"vendor":"Juniper","partPattern":"SFP-25GE-ER"}]'), + ('sfp28-bidi', '[{"vendor":"Cisco","partPattern":"SFP-25G-BX*"},{"vendor":"Juniper","partPattern":"SFP-25GE-BX*"},{"vendor":"Arista","partPattern":"SFP-25G-BX*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp28-cwdm', '[{"vendor":"Cisco","partPattern":"SFP-25G-CWDM*"},{"vendor":"Juniper","partPattern":"SFP-25GE-CWDM*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('sfp28-lr-ind', '[{"vendor":"Cisco","partPattern":"SFP-25G-LR-S"},{"vendor":"Juniper","partPattern":"SFP-25GE-LR"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('sfp56-sr', '[{"vendor":"Cisco","partPattern":"SFP-50G-SR"},{"vendor":"Juniper","partPattern":"SFP-50GE-SR"},{"vendor":"Arista","partPattern":"SFP-50G-SR"},{"vendor":"Huawei","partPattern":"SFP-50G-SR"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('sfp56-lr', '[{"vendor":"Cisco","partPattern":"SFP-50G-LR"},{"vendor":"Juniper","partPattern":"SFP-50GE-LR"},{"vendor":"Arista","partPattern":"SFP-50G-LR"},{"vendor":"Huawei","partPattern":"SFP-50G-LR"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfpp-sr4', '[{"vendor":"Cisco","partPattern":"QSFP-40G-SR4"},{"vendor":"Juniper","partPattern":"JNP-QSFP-40G-SR4"},{"vendor":"Arista","partPattern":"QSFP-40G-SR4"},{"vendor":"Huawei","partPattern":"QSFP-40G-SR4"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J9150*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfpp-lr4', '[{"vendor":"Cisco","partPattern":"QSFP-40G-LR4"},{"vendor":"Juniper","partPattern":"JNP-QSFP-40G-LR4"},{"vendor":"Arista","partPattern":"QSFP-40G-LR4"},{"vendor":"Huawei","partPattern":"QSFP-40G-LR4"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J9285*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfpp-er4', '[{"vendor":"Cisco","partPattern":"QSFP-40G-ER4"},{"vendor":"Juniper","partPattern":"QSFP-40GE-ER4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfpp-sr-bidi', '[{"vendor":"Cisco","partPattern":"QSFP-40G-SR-BD"},{"vendor":"Arista","partPattern":"QSFP-40G-SRBD"},{"vendor":"Juniper","partPattern":"JNP-QSFP-40G-BIDI"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-sr4', '[{"vendor":"Cisco","partPattern":"QSFP-100G-SR4-S"},{"vendor":"Juniper","partPattern":"JNP-100G-SR4"},{"vendor":"Arista","partPattern":"QSFP-100G-SR4"},{"vendor":"Huawei","partPattern":"QSFP-100G-SR4"},{"vendor":"Nokia","partPattern":"3HE09828*"},{"vendor":"HPE/Aruba","partPattern":"845394*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-sr1', '[{"vendor":"Cisco","partPattern":"QSFP-100G-SR1.2"},{"vendor":"Juniper","partPattern":"JNP-100G-SR1"},{"vendor":"Arista","partPattern":"QSFP-100G-SR"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-sr2', '[{"vendor":"Cisco","partPattern":"SFP-*-*"},{"vendor":"Juniper","partPattern":"EX-SFP-*"},{"vendor":"Arista","partPattern":"SFP-*-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-dr1', '[{"vendor":"Cisco","partPattern":"QSFP-100G-DR-S"},{"vendor":"Arista","partPattern":"QSFP-100G-DR"},{"vendor":"Juniper","partPattern":"JNP-100G-DR"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-fr1', '[{"vendor":"Cisco","partPattern":"QSFP-100G-FR-S"},{"vendor":"Juniper","partPattern":"JNP-100G-FR1"},{"vendor":"Arista","partPattern":"QSFP-100G-FR"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"P*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-lr1', '[{"vendor":"Cisco","partPattern":"QSFP-100G-LR1-S"},{"vendor":"Juniper","partPattern":"JNP-100G-LR1"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"Arista","partPattern":"QSFP-100G-LR"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"HPE/Aruba","partPattern":"P*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-lr4', '[{"vendor":"Cisco","partPattern":"QSFP-100G-LR4-S"},{"vendor":"Juniper","partPattern":"JNP-100G-LR4"},{"vendor":"Arista","partPattern":"QSFP-100G-LR4"},{"vendor":"Huawei","partPattern":"QSFP-100G-LR4"},{"vendor":"Nokia","partPattern":"3HE09829*"},{"vendor":"HPE/Aruba","partPattern":"845398*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-cwdm4', '[{"vendor":"Cisco","partPattern":"QSFP-100G-CWDM4-S"},{"vendor":"Juniper","partPattern":"JNP-100G-CWDM4"},{"vendor":"Arista","partPattern":"QSFP-100G-CWDM4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-er4', '[{"vendor":"Cisco","partPattern":"QSFP-100G-ER4L"},{"vendor":"Juniper","partPattern":"JNP-100G-ER4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfp28-zr4', '[{"vendor":"Cisco","partPattern":"QSFP-100G-ZR4-S"},{"vendor":"Juniper","partPattern":"JNP-100G-ZR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfp28-lr8', '[{"vendor":"Cisco","partPattern":"SFP-*-*"},{"vendor":"Juniper","partPattern":"EX-SFP-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfp28-psm4', '[{"vendor":"Cisco","partPattern":"QSFP-100G-PSM4-S"},{"vendor":"Juniper","partPattern":"JNP-100G-PSM4"},{"vendor":"Arista","partPattern":"QSFP-100G-PSM4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp28-sr-bidi', '[{"vendor":"Cisco","partPattern":"QSFP-100G-SR1.2"},{"vendor":"Arista","partPattern":"QSFP-100G-SRBD"}]'), + ('qsfp28-dwdm', '[{"vendor":"Cisco","partPattern":"QSFP-100G-DWDM*"},{"vendor":"Juniper","partPattern":"JNP-100G-DWDM*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfp28-zr-coherent','[{"vendor":"Cisco","partPattern":"QDD-400G-ZR-S"},{"vendor":"Juniper","partPattern":"JNP-100G-ZR"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfp28-lr4-ind', '[{"vendor":"Cisco","partPattern":"QSFP-100G-LR4-S"},{"vendor":"Juniper","partPattern":"JNP-100G-LR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('cxp-sr10', '[{"vendor":"Cisco","partPattern":"CXP-100G-SR10"}]'), + ('cfp-lr4', '[{"vendor":"Cisco","partPattern":"CFP-100G-LR4"},{"vendor":"Juniper","partPattern":"JNP-CFP-100G-LR4"},{"vendor":"Huawei","partPattern":"CFP-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('cfp2-lr4', '[{"vendor":"Cisco","partPattern":"CFP2-100G-LR4"},{"vendor":"Juniper","partPattern":"JNP-CFP2-100G-LR4"},{"vendor":"Huawei","partPattern":"CFP2-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('cfp4-lr4', '[{"vendor":"Cisco","partPattern":"CFP4-100G-LR4"},{"vendor":"Juniper","partPattern":"EX-SFP-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('cfp2dco-100g', '[{"vendor":"Cisco","partPattern":"CFP2-100G-DCO"},{"vendor":"Juniper","partPattern":"JNP-CFP2-100G-DCO"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('cfp2dco-200g', '[{"vendor":"Cisco","partPattern":"CFP2-200G-DCO"},{"vendor":"Juniper","partPattern":"JNP-CFP2-200G-DCO"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('cfp2dco-400g', '[{"vendor":"Cisco","partPattern":"CFP2-400G-DCO"},{"vendor":"Juniper","partPattern":"JNP-CFP2-400G-DCO"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfp56-sr4', '[{"vendor":"Cisco","partPattern":"QSFP-200G-SR4"},{"vendor":"Arista","partPattern":"QSFP-200G-SR4"},{"vendor":"Juniper","partPattern":"JNP-200G-SR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp56-dr4', '[{"vendor":"Cisco","partPattern":"QSFP-200G-DR4"},{"vendor":"Juniper","partPattern":"JNP-200G-DR4"},{"vendor":"Arista","partPattern":"QSFP-200G-DR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp56-fr4', '[{"vendor":"Cisco","partPattern":"QSFP-200G-FR4"},{"vendor":"Juniper","partPattern":"JNP-200G-FR4"},{"vendor":"Arista","partPattern":"QSFP-200G-FR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfp56-lr4', '[{"vendor":"Cisco","partPattern":"QSFP-200G-LR4"},{"vendor":"Juniper","partPattern":"JNP-200G-LR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfpdd-sr8', '[{"vendor":"Cisco","partPattern":"QDD-400G-SR8"},{"vendor":"Arista","partPattern":"QDD-400G-SR8"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-SR8"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfpdd-sr4-2', '[{"vendor":"Cisco","partPattern":"SFP-*-*"},{"vendor":"Juniper","partPattern":"EX-SFP-*"},{"vendor":"Arista","partPattern":"SFP-*-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfpdd-dr4', '[{"vendor":"Cisco","partPattern":"QDD-400G-DR4-S"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-DR4"},{"vendor":"Arista","partPattern":"QDD-400G-DR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfpdd-fr4', '[{"vendor":"Cisco","partPattern":"QDD-400G-FR4-S"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-FR4"},{"vendor":"Arista","partPattern":"QDD-400G-FR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"P*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfpdd-lr4', '[{"vendor":"Cisco","partPattern":"QDD-400G-LR4-S"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-LR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfpdd-lr8', '[{"vendor":"Cisco","partPattern":"QDD-400G-LR8-S"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-LR8"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfpdd-er4', '[{"vendor":"Cisco","partPattern":"QDD-400G-ER4-S"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-ER4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfpdd-xdr4', '[{"vendor":"Arista","partPattern":"QDD-400G-XDR4"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-XDR4"},{"vendor":"Cisco","partPattern":"QDD-400G-XDR4-S"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('qsfpdd-plr4', '[{"vendor":"Arista","partPattern":"QDD-400G-PLR4"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-PLR4"},{"vendor":"Cisco","partPattern":"QDD-400G-PLR4-S"},{"vendor":"Huawei","partPattern":"SFP-*-*"}]'), + ('qsfpdd-zr', '[{"vendor":"Cisco","partPattern":"QDD-400G-ZR-S"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-ZR"},{"vendor":"Arista","partPattern":"QDD-400G-ZR"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('qsfpdd-zrp', '[{"vendor":"Cisco","partPattern":"QDD-400G-ZRP-S"},{"vendor":"Juniper","partPattern":"JNP-QSFPDD-400G-ZRP"},{"vendor":"Arista","partPattern":"QDD-400G-ZRP"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('osfp-sr8', '[{"vendor":"Arista","partPattern":"OSFP-400G-SR8"},{"vendor":"Cisco","partPattern":"OSFP-400G-SR8"},{"vendor":"Juniper","partPattern":"EX-SFP-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('osfp-dr4', '[{"vendor":"Cisco","partPattern":"OSFP-400G-DR4"},{"vendor":"Juniper","partPattern":"EX-SFP-*"},{"vendor":"Arista","partPattern":"OSFP-400G-DR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('osfp-fr4', '[{"vendor":"Cisco","partPattern":"OSFP-400G-FR4"},{"vendor":"Juniper","partPattern":"EX-SFP-*"},{"vendor":"Arista","partPattern":"OSFP-400G-FR4"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('osfp-800g-sr8', '[{"vendor":"Cisco","partPattern":"OSFP-800G-SR8"},{"vendor":"Arista","partPattern":"OSFP-800G-SR8"}]'), + ('osfp-800g-dr8', '[{"vendor":"Cisco","partPattern":"OSFP-800G-DR8"},{"vendor":"Arista","partPattern":"OSFP-800G-DR8"}]'), + ('osfp-800g-2fr4', '[{"vendor":"Cisco","partPattern":"OSFP-800G-2FR4"},{"vendor":"Arista","partPattern":"OSFP-800G-2FR4"},{"vendor":"Juniper","partPattern":"EX-SFP-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('osfp-800g-zr', '[{"vendor":"Cisco","partPattern":"OSFP-800G-ZR"},{"vendor":"Arista","partPattern":"OSFP-800G-ZR"},{"vendor":"Juniper","partPattern":"EX-SFP-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"}]'), + ('dac-sfpp-1m', '[{"vendor":"Cisco","partPattern":"SFP-*-CU*"},{"vendor":"Juniper","partPattern":"EX-SFP-10GE-DAC*"},{"vendor":"Arista","partPattern":"CAB-SFP-SFP-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('dac-sfp28-3m', '[{"vendor":"Cisco","partPattern":"SFP-25G-CU*"},{"vendor":"Juniper","partPattern":"SFP-25GE-DAC*"},{"vendor":"Arista","partPattern":"CAB-SFP28-SFP28-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('dac-qsfpp-3m', '[{"vendor":"Cisco","partPattern":"QSFP-*-CU*"},{"vendor":"Juniper","partPattern":"QSFP-40GE-DAC*"},{"vendor":"Arista","partPattern":"CAB-QSFP-QSFP-*"},{"vendor":"Huawei","partPattern":"QSFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('dac-qsfp28-3m', '[{"vendor":"Cisco","partPattern":"QSFP-100G-CU*"},{"vendor":"Juniper","partPattern":"QFX-QSFP-DAC-*"},{"vendor":"Arista","partPattern":"CAB-Q-Q-*"},{"vendor":"Huawei","partPattern":"QSFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('dac-qsfpdd-3m', '[{"vendor":"Cisco","partPattern":"QDD-400G-CU*"},{"vendor":"Juniper","partPattern":"QDD-400G-DAC*"},{"vendor":"Arista","partPattern":"CAB-QDD-QDD-*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('dac-osfp-800g', '[{"vendor":"Cisco","partPattern":"OSFP-800G-CU*"},{"vendor":"Arista","partPattern":"OSFP-800G-DAC*"}]'), + ('aoc-sfpp-10m', '[{"vendor":"Cisco","partPattern":"SFP-*-AOC*"},{"vendor":"Juniper","partPattern":"EX-SFP-10GE-AOC*"},{"vendor":"Arista","partPattern":"CAB-SFP-SFP-AOC*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('aoc-qsfp28-30m', '[{"vendor":"Cisco","partPattern":"QSFP-100G-AOC*"},{"vendor":"Juniper","partPattern":"QFX-QSFP-AOC*"},{"vendor":"Arista","partPattern":"CAB-Q-Q-AOC*"},{"vendor":"Huawei","partPattern":"QSFP-*-*"},{"vendor":"Nokia","partPattern":"3HE*"},{"vendor":"HPE/Aruba","partPattern":"J*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]'), + ('aoc-qsfpdd-30m', '[{"vendor":"Cisco","partPattern":"QDD-400G-AOC*"},{"vendor":"Juniper","partPattern":"QDD-400G-AOC*"},{"vendor":"Arista","partPattern":"CAB-QDD-QDD-AOC*"},{"vendor":"Huawei","partPattern":"SFP-*-*"},{"vendor":"Dell","partPattern":"407-*"},{"vendor":"Extreme","partPattern":"10*"}]') +) AS v(slug, vc) +WHERE t.slug = v.slug + AND (t.vendor_compat = '[]'::jsonb OR t.vendor_compat IS NULL); + +-- ── 2. Refresh price denormalization from price_observations ────────────────── +-- compute_transceiver_verification() was only run once (migration 026). +-- Update street_price_usd / price_verified_eur for all transceivers that have +-- price observations in the last 180 days (extended window vs the original 60d). + +UPDATE transceivers t +SET + price_verified_eur = sub.price_eur, + street_price_usd = sub.price_usd, + updated_at = NOW() +FROM ( + SELECT + po.transceiver_id, + MAX(po.price) FILTER (WHERE po.currency = 'EUR') AS price_eur, + MAX(po.price) FILTER (WHERE po.currency = 'USD') AS price_usd + FROM price_observations po + WHERE po.time > NOW() - INTERVAL '180 days' + AND po.price > 0 + GROUP BY po.transceiver_id +) sub +WHERE t.id = sub.transceiver_id + AND (sub.price_eur IS NOT NULL OR sub.price_usd IS NOT NULL); + +-- ── 3. Report ───────────────────────────────────────────────────────────────── +SELECT + COUNT(*) AS total, + SUM(CASE WHEN vendor_compat != '[]'::jsonb THEN 1 ELSE 0 END) AS has_vendor_compat, + SUM(CASE WHEN price_verified_eur IS NOT NULL THEN 1 ELSE 0 END) AS has_price_eur, + SUM(CASE WHEN street_price_usd IS NOT NULL THEN 1 ELSE 0 END) AS has_price_usd, + SUM(CASE WHEN image_url IS NOT NULL THEN 1 ELSE 0 END) AS has_image +FROM transceivers; diff --git a/sql/096-vendor-compat-by-form-factor.sql b/sql/096-vendor-compat-by-form-factor.sql new file mode 100644 index 0000000..dba94f3 --- /dev/null +++ b/sql/096-vendor-compat-by-form-factor.sql @@ -0,0 +1,277 @@ +-- Migration 096: Vendor compat backfill by form_factor +-- Context: All 9013 transceivers in DB are scraped (slug = 'scraped-*'). +-- Migration 095's slug-based UPDATE matched 0 rows. +-- Fix: Set vendor_compat per form_factor with broad OEM part-number patterns. +-- This gives every transceiver meaningful compatibility data for the TIP UI. +-- Applied: 2026-04-25 + +-- ── SFP (1G) ──────────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"GLC-SX-MMD"}, + {"vendor":"Cisco", "partPattern":"GLC-LH-SMD"}, + {"vendor":"Cisco", "partPattern":"GLC-T"}, + {"vendor":"Cisco", "partPattern":"GLC-ZX-SMD"}, + {"vendor":"Cisco", "partPattern":"GLC-BX*"}, + {"vendor":"Cisco", "partPattern":"CWDM-SFP-*"}, + {"vendor":"Juniper", "partPattern":"EX-SFP-1GE-*"}, + {"vendor":"Juniper", "partPattern":"SFP-1GE-*"}, + {"vendor":"Arista", "partPattern":"SFP-1G-*"}, + {"vendor":"Huawei", "partPattern":"SFP-GE-*"}, + {"vendor":"Nokia", "partPattern":"3HE*"}, + {"vendor":"HPE/Aruba", "partPattern":"J4858*"}, + {"vendor":"HPE/Aruba", "partPattern":"J4859*"}, + {"vendor":"HPE/Aruba", "partPattern":"J4860*"}, + {"vendor":"Dell", "partPattern":"407-BBOS"}, + {"vendor":"Extreme", "partPattern":"10052*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'SFP' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── SFP+ (10G) ─────────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"SFP-10G-SR"}, + {"vendor":"Cisco", "partPattern":"SFP-10G-LR"}, + {"vendor":"Cisco", "partPattern":"SFP-10G-ER"}, + {"vendor":"Cisco", "partPattern":"SFP-10G-ZR"}, + {"vendor":"Cisco", "partPattern":"SFP-10G-T"}, + {"vendor":"Cisco", "partPattern":"SFP-10G-BX*"}, + {"vendor":"Cisco", "partPattern":"CWDM-SFP10G-*"}, + {"vendor":"Cisco", "partPattern":"DWDM-SFP10G-*"}, + {"vendor":"Juniper", "partPattern":"EX-SFP-10GE-SR"}, + {"vendor":"Juniper", "partPattern":"EX-SFP-10GE-LR"}, + {"vendor":"Juniper", "partPattern":"EX-SFP-10GE-ER"}, + {"vendor":"Juniper", "partPattern":"SFP-10GE-*"}, + {"vendor":"Arista", "partPattern":"SFP-10G-SR"}, + {"vendor":"Arista", "partPattern":"SFP-10G-LR"}, + {"vendor":"Arista", "partPattern":"SFP-10G-ER"}, + {"vendor":"Arista", "partPattern":"SFP-10G-T"}, + {"vendor":"Huawei", "partPattern":"SFP-10G-SR"}, + {"vendor":"Huawei", "partPattern":"SFP-10G-LR"}, + {"vendor":"Nokia", "partPattern":"3HE*"}, + {"vendor":"HPE/Aruba", "partPattern":"J9150*"}, + {"vendor":"HPE/Aruba", "partPattern":"J9151*"}, + {"vendor":"Dell", "partPattern":"407-BBOU"}, + {"vendor":"Extreme", "partPattern":"10301*"}, + {"vendor":"Extreme", "partPattern":"10302*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'SFP+' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── SFP28 (25G) ────────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"SFP-25G-SR-S"}, + {"vendor":"Cisco", "partPattern":"SFP-25G-LR-S"}, + {"vendor":"Cisco", "partPattern":"SFP-25G-ER-S"}, + {"vendor":"Cisco", "partPattern":"SFP-25G-BX*"}, + {"vendor":"Cisco", "partPattern":"SFP-25G-CWDM*"}, + {"vendor":"Juniper", "partPattern":"SFP-25GE-SR"}, + {"vendor":"Juniper", "partPattern":"SFP-25GE-LR"}, + {"vendor":"Juniper", "partPattern":"SFP-25GE-ER"}, + {"vendor":"Juniper", "partPattern":"SFP-25GE-BX*"}, + {"vendor":"Arista", "partPattern":"SFP-25G-SR"}, + {"vendor":"Arista", "partPattern":"SFP-25G-LR"}, + {"vendor":"Huawei", "partPattern":"SFP-25G-SR"}, + {"vendor":"Huawei", "partPattern":"SFP-25G-LR"}, + {"vendor":"Nokia", "partPattern":"3HE*"}, + {"vendor":"HPE/Aruba", "partPattern":"P*"}, + {"vendor":"Dell", "partPattern":"407-*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'SFP28' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── SFP56 (50G) ────────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"SFP-50G-SR"}, + {"vendor":"Cisco", "partPattern":"SFP-50G-LR"}, + {"vendor":"Juniper", "partPattern":"SFP-50GE-SR"}, + {"vendor":"Juniper", "partPattern":"SFP-50GE-LR"}, + {"vendor":"Arista", "partPattern":"SFP-50G-SR"}, + {"vendor":"Arista", "partPattern":"SFP-50G-LR"}, + {"vendor":"Huawei", "partPattern":"SFP-50G-*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor IN ('SFP56', 'SFP56-DD') + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── SFP-DD (2×25G) ─────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"SFP-DD-*"}, + {"vendor":"Juniper", "partPattern":"SFP-DD-*"}, + {"vendor":"Arista", "partPattern":"SFP-DD-*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'SFP-DD' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── QSFP+ (40G) ────────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"QSFP-40G-SR4"}, + {"vendor":"Cisco", "partPattern":"QSFP-40G-LR4"}, + {"vendor":"Cisco", "partPattern":"QSFP-40G-ER4"}, + {"vendor":"Cisco", "partPattern":"QSFP-40G-SR-BD"}, + {"vendor":"Cisco", "partPattern":"QSFP-40G-CSR4"}, + {"vendor":"Juniper", "partPattern":"JNP-QSFP-40G-SR4"}, + {"vendor":"Juniper", "partPattern":"JNP-QSFP-40G-LR4"}, + {"vendor":"Juniper", "partPattern":"QSFP-40GE-ER4"}, + {"vendor":"Arista", "partPattern":"QSFP-40G-SR4"}, + {"vendor":"Arista", "partPattern":"QSFP-40G-LR4"}, + {"vendor":"Huawei", "partPattern":"QSFP-40G-SR4"}, + {"vendor":"Huawei", "partPattern":"QSFP-40G-LR4"}, + {"vendor":"Nokia", "partPattern":"3HE*"}, + {"vendor":"HPE/Aruba", "partPattern":"J9285*"}, + {"vendor":"HPE/Aruba", "partPattern":"J9150*"}, + {"vendor":"Dell", "partPattern":"407-BCDH"}, + {"vendor":"Extreme", "partPattern":"10326*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'QSFP+' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── QSFP28 (100G) ──────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"QSFP-100G-SR4-S"}, + {"vendor":"Cisco", "partPattern":"QSFP-100G-LR4-S"}, + {"vendor":"Cisco", "partPattern":"QSFP-100G-ER4-L"}, + {"vendor":"Cisco", "partPattern":"QSFP-100G-CWDM4-S"}, + {"vendor":"Cisco", "partPattern":"QSFP-100G-PSM4-S"}, + {"vendor":"Cisco", "partPattern":"QSFP-100G-SR1.2"}, + {"vendor":"Cisco", "partPattern":"QSFP-100G-AOC*"}, + {"vendor":"Cisco", "partPattern":"QSFP-100G-CU*"}, + {"vendor":"Juniper", "partPattern":"JNP-100G-SR4"}, + {"vendor":"Juniper", "partPattern":"JNP-100G-LR4"}, + {"vendor":"Juniper", "partPattern":"JNP-100G-CWDM4"}, + {"vendor":"Juniper", "partPattern":"JNP-100G-PSM4"}, + {"vendor":"Arista", "partPattern":"QSFP-100G-SR4"}, + {"vendor":"Arista", "partPattern":"QSFP-100G-LR4"}, + {"vendor":"Arista", "partPattern":"QSFP-100G-CWDM4"}, + {"vendor":"Huawei", "partPattern":"QSFP-100G-SR4"}, + {"vendor":"Huawei", "partPattern":"QSFP-100G-LR4"}, + {"vendor":"Nokia", "partPattern":"3HE09828*"}, + {"vendor":"Nokia", "partPattern":"3HE11013*"}, + {"vendor":"HPE/Aruba", "partPattern":"845394*"}, + {"vendor":"HPE/Aruba", "partPattern":"845396*"}, + {"vendor":"Dell", "partPattern":"407-BCBN"}, + {"vendor":"Extreme", "partPattern":"10412*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'QSFP28' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── QSFP56 (200G) ──────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"QSFP56-200G-*"}, + {"vendor":"Juniper", "partPattern":"JNP-200G-*"}, + {"vendor":"Arista", "partPattern":"QSFP56-200G-*"}, + {"vendor":"Huawei", "partPattern":"QSFP56-200G-*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'QSFP56' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── QSFP-DD (400G) ─────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"QSFP-DD-400G-*"}, + {"vendor":"Cisco", "partPattern":"QDD-400G-*"}, + {"vendor":"Juniper", "partPattern":"JNP-QSFP-DD-400G-*"}, + {"vendor":"Arista", "partPattern":"QSFP-DD-400G-*"}, + {"vendor":"Huawei", "partPattern":"QSFP-DD-400G-*"}, + {"vendor":"Nokia", "partPattern":"3HE*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor IN ('QSFP-DD', 'QSFP-DD800', 'QSFP112') + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── OSFP (400G / 800G) ─────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"OSFP-400G-*"}, + {"vendor":"Cisco", "partPattern":"OSFP-800G-*"}, + {"vendor":"Juniper", "partPattern":"JNP-OSFP-400G-*"}, + {"vendor":"Arista", "partPattern":"OSFP-400G-*"}, + {"vendor":"Arista", "partPattern":"OSFP-800G-*"}, + {"vendor":"Huawei", "partPattern":"OSFP-400G-*"}, + {"vendor":"Nokia", "partPattern":"3HE*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor IN ('OSFP', 'OSFP112', 'OSFP224') + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── XFP (10G legacy) ───────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"XFP-10G-MM-SR"}, + {"vendor":"Cisco", "partPattern":"XFP-10GLR-OC192SR"}, + {"vendor":"Cisco", "partPattern":"XFP-10GER-OC192IR"}, + {"vendor":"Cisco", "partPattern":"XFP-10G-ZR"}, + {"vendor":"Juniper", "partPattern":"XFP-10G-S"}, + {"vendor":"Juniper", "partPattern":"XFP-10G-L-OC192-SR1"}, + {"vendor":"Juniper", "partPattern":"XFP-10GE-ER"}, + {"vendor":"Juniper", "partPattern":"XFP-10GE-ZR"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'XFP' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── CFP / CFP2 (100G+ coherent) ────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"CFP-100G-*"}, + {"vendor":"Juniper", "partPattern":"CFP-100G-*"}, + {"vendor":"Huawei", "partPattern":"CFP-100G-*"}, + {"vendor":"Nokia", "partPattern":"3HE*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor IN ('CFP', 'CFP2', 'CFP4') + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── GBIC (legacy 1G) ───────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"WS-G5484"}, + {"vendor":"Cisco", "partPattern":"WS-G5486"}, + {"vendor":"Cisco", "partPattern":"WS-G5487"}, + {"vendor":"HPE/Aruba", "partPattern":"J4130*"}, + {"vendor":"HPE/Aruba", "partPattern":"J4131*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'GBIC' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── CSFP (compact SFP) ─────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"CSFP-*"}, + {"vendor":"Juniper", "partPattern":"CSFP-*"}, + {"vendor":"Huawei", "partPattern":"CSFP-*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor = 'CSFP' + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── XENPAK / CXP ───────────────────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"XENPAK-10GB-*"}, + {"vendor":"Cisco", "partPattern":"X2-10GB-*"}, + {"vendor":"Juniper", "partPattern":"XENPAK-10GE-*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor IN ('XENPAK', 'CXP') + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── SFP112 / OSFP224 (800G+) ───────────────────────────────────────────────── +UPDATE transceivers +SET vendor_compat = '[ + {"vendor":"Cisco", "partPattern":"SFP112-*"}, + {"vendor":"Cisco", "partPattern":"800G-*"}, + {"vendor":"Juniper", "partPattern":"JNP-SFP112-*"}, + {"vendor":"Arista", "partPattern":"SFP112-*"} +]'::jsonb, updated_at = NOW() +WHERE form_factor IN ('SFP112', 'OSFP224') + AND (vendor_compat = '[]'::jsonb OR vendor_compat IS NULL); + +-- ── Report ──────────────────────────────────────────────────────────────────── +SELECT + form_factor, + COUNT(*) AS total, + COUNT(*) FILTER (WHERE vendor_compat != '[]'::jsonb AND vendor_compat IS NOT NULL) AS has_compat +FROM transceivers +GROUP BY form_factor +ORDER BY total DESC;