/** * Spec-Based Equivalence Matcher * * Matches FX products with competitor products by technical specification * when no OPN-based equivalence exists. Spec-matching is a fallback: * OPN-confirmed matches (confidence=1.0) always take priority. * * Match criteria: * - Same form_factor (exact) * - Same speed_gbps (exact) * - Same reach tier (SR/IR/LR/ER/ZR) * - Same primary wavelength within ±10nm (CWDM/WDM safe) * OR both have no wavelength data (broadband products) * - Max 30 competitor matches per FX product (safety cap) * * Match quality: * confidence = 0.85 * match_basis = '{spec}' * status = 'auto_approved' */ import { pool } from "../utils/db"; export interface SpecMatcherResult { inserted: number; fxProductsScanned: number; candidatePairs: number; skippedExisting: number; } // ── Queries ────────────────────────────────────────────────────────────────── const INSERT_SPEC_MATCHES = ` INSERT INTO transceiver_equivalences ( flexoptix_id, competitor_id, confidence, status, match_basis, match_notes, created_at, updated_at ) SELECT DISTINCT fx.id AS flexoptix_id, comp.id AS competitor_id, 0.85 AS confidence, 'auto_approved' AS status, ARRAY['spec'] AS match_basis, 'Spec match: ' || fx.form_factor || ' ' || fx.speed_gbps || 'G ' || CASE WHEN fx.reach_meters <= 300 THEN 'SR' WHEN fx.reach_meters <= 2000 THEN 'IR' WHEN fx.reach_meters <= 10000 THEN 'LR' WHEN fx.reach_meters <= 40000 THEN 'ER' ELSE 'ZR' END || CASE WHEN tip_extract_wavelength_nm(fx.wavelengths) IS NOT NULL THEN ' @' || tip_extract_wavelength_nm(fx.wavelengths) || 'nm' ELSE '' END AS match_notes, NOW() AS created_at, NOW() AS updated_at FROM transceivers fx JOIN vendors vfx ON vfx.id = fx.vendor_id AND UPPER(vfx.name) LIKE '%FLEXOPTIX%' JOIN transceivers comp ON comp.form_factor = fx.form_factor AND comp.speed_gbps = fx.speed_gbps AND comp.reach_meters >= 10 AND tip_reach_tier(comp.reach_meters) = tip_reach_tier(fx.reach_meters) AND ( (tip_extract_wavelength_nm(fx.wavelengths) IS NULL AND tip_extract_wavelength_nm(comp.wavelengths) IS NULL) OR ABS( COALESCE(tip_extract_wavelength_nm(comp.wavelengths), 0) - COALESCE(tip_extract_wavelength_nm(fx.wavelengths), 0) ) <= 10 ) JOIN vendors vcomp ON vcomp.id = comp.vendor_id AND vcomp.is_competitor = true WHERE fx.reach_meters >= 10 AND fx.speed_gbps > 0 -- OPN match already exists → skip (spec is fallback only) AND NOT EXISTS ( SELECT 1 FROM transceiver_equivalences e WHERE e.flexoptix_id = fx.id AND 'opn' = ANY(e.match_basis) ) -- Skip pairs that already have ANY equivalence AND NOT EXISTS ( SELECT 1 FROM transceiver_equivalences e WHERE e.flexoptix_id = fx.id AND e.competitor_id = comp.id ) -- Safety cap: skip if > 30 competitors would match (too generic) AND ( SELECT COUNT(DISTINCT c2.id) FROM transceivers c2 JOIN vendors vc2 ON vc2.id = c2.vendor_id AND vc2.is_competitor = true WHERE c2.form_factor = fx.form_factor AND c2.speed_gbps = fx.speed_gbps AND c2.reach_meters >= 10 AND tip_reach_tier(c2.reach_meters) = tip_reach_tier(fx.reach_meters) AND ( (tip_extract_wavelength_nm(fx.wavelengths) IS NULL AND tip_extract_wavelength_nm(c2.wavelengths) IS NULL) OR ABS( COALESCE(tip_extract_wavelength_nm(c2.wavelengths), 0) - COALESCE(tip_extract_wavelength_nm(fx.wavelengths), 0) ) <= 10 ) ) <= 30 ON CONFLICT DO NOTHING `; const COUNT_FX_WITHOUT_OPN = ` SELECT COUNT(DISTINCT t.id) AS cnt FROM transceivers t JOIN vendors v ON v.id = t.vendor_id AND UPPER(v.name) LIKE '%FLEXOPTIX%' WHERE t.reach_meters >= 10 AND t.speed_gbps > 0 AND NOT EXISTS ( SELECT 1 FROM transceiver_equivalences e WHERE e.flexoptix_id = t.id AND 'opn' = ANY(e.match_basis) ) `; const COUNT_SPEC_CANDIDATES = ` SELECT COUNT(DISTINCT (fx.id, comp.id)) AS cnt FROM transceivers fx JOIN vendors vfx ON vfx.id = fx.vendor_id AND UPPER(vfx.name) LIKE '%FLEXOPTIX%' JOIN transceivers comp ON comp.form_factor = fx.form_factor AND comp.speed_gbps = fx.speed_gbps AND comp.reach_meters >= 10 AND tip_reach_tier(comp.reach_meters) = tip_reach_tier(fx.reach_meters) AND ( (tip_extract_wavelength_nm(fx.wavelengths) IS NULL AND tip_extract_wavelength_nm(comp.wavelengths) IS NULL) OR ABS( COALESCE(tip_extract_wavelength_nm(comp.wavelengths), 0) - COALESCE(tip_extract_wavelength_nm(fx.wavelengths), 0) ) <= 10 ) JOIN vendors vcomp ON vcomp.id = comp.vendor_id AND vcomp.is_competitor = true WHERE fx.reach_meters >= 10 AND fx.speed_gbps > 0 AND NOT EXISTS ( SELECT 1 FROM transceiver_equivalences e WHERE e.flexoptix_id = fx.id AND 'opn' = ANY(e.match_basis) ) `; // ── Main export ─────────────────────────────────────────────────────────────── export async function runSpecMatcher(): Promise { const ts = () => new Date().toISOString(); console.log(`[${ts()}] Spec Matcher starting`); const fxRes = await pool.query<{ cnt: string }>(COUNT_FX_WITHOUT_OPN); const fxProductsScanned = parseInt(fxRes.rows[0].cnt, 10); const candRes = await pool.query<{ cnt: string }>(COUNT_SPEC_CANDIDATES); const candidatePairs = parseInt(candRes.rows[0].cnt, 10); console.log( `[${ts()}] Spec Matcher: ${fxProductsScanned} FX products without OPN, ` + `${candidatePairs} spec candidate pairs`, ); const insertRes = await pool.query(INSERT_SPEC_MATCHES); const inserted = insertRes.rowCount ?? 0; const skippedExisting = candidatePairs - inserted; console.log( `[${ts()}] Spec Matcher done: ${inserted} new spec equivalences inserted ` + `(${skippedExisting} pairs already existed or capped)`, ); return { inserted, fxProductsScanned, candidatePairs, skippedExisting }; }