fix: vendor_compat 0%→100%, price denorm, wiitek disabled, price-denorm scheduler

- 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)
This commit is contained in:
Rene Fichtmueller 2026-04-25 08:55:21 +02:00
parent 108b2687d6
commit ba998f4c01
6 changed files with 564 additions and 80 deletions

View File

@ -147,6 +147,8 @@ export async function registerSchedules(boss: PgBoss): Promise<void> {
"sync:nas", "sync:nas",
// ── Health Monitoring ───────────────────────────────────────────── // ── Health Monitoring ─────────────────────────────────────────────
"monitor:scraper-health", "monitor:scraper-health",
// ── Price denormalization refresh ─────────────────────────────────
"compute:price-denorm",
// ── Verification Reconciliation ─────────────────────────────────── // ── Verification Reconciliation ───────────────────────────────────
"maintenance:reconcile-verification", "maintenance:reconcile-verification",
// ── Competitor Equivalence Matching ─────────────────────────────── // ── Competitor Equivalence Matching ───────────────────────────────
@ -273,6 +275,8 @@ export async function registerSchedules(boss: PgBoss): Promise<void> {
await boss.schedule("compute:abc", "50 3,7,11,15,19,23 * * *", {}, { retryLimit: 2, expireInSeconds: 600 }); 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 }); 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 // PREDICTION SIGNAL SCRAPERS
@ -319,7 +323,7 @@ export async function registerSchedules(boss: PgBoss): Promise<void> {
// Re-research approved equivalences: daily at 03:00 UTC, processes 200 items per run // 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 }); 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<void> { export async function registerWorkers(boss: PgBoss): Promise<void> {
@ -753,6 +757,36 @@ export async function registerWorkers(boss: PgBoss): Promise<void> {
await scrapeMouser(); 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 ────────────────────────────────────────────────────── // ── Health monitor ──────────────────────────────────────────────────────
await boss.work("monitor:scraper-health", async () => { await boss.work("monitor:scraper-health", async () => {
const { pool } = await import("./utils/db"); const { pool } = await import("./utils/db");
@ -1174,5 +1208,5 @@ export async function registerWorkers(boss: PgBoss): Promise<void> {
console.log(`[re-research] confirmed: ${confirmed}, reverted to pending: ${reverted}, batch size: ${batch.rows.length}`); 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)");
} }

View File

@ -7,83 +7,8 @@
* *
* Schedule: every 8h * 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<void> { export async function scrapeWiitek(): Promise<void> {
logger.info("Wiitek scraper starting"); // wiitek.com unreachable since 2026-04 (ConnectTimeoutError on all requests)
const vendorId = await ensureVendor("Wiitek", BASE); console.warn("[wiitek] Scraper disabled — www.wiitek.com:443 connection timeout on every attempt (2026-04-25)");
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`);
} }

View File

@ -181,7 +181,9 @@ async function seedTransceivers(
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
$11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20,
$21, $22, $23, $24, $25, $26, $27, $28, $29 $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.id,
t.standard, t.standard,

View File

@ -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');

View File

@ -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;

View File

@ -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;