-- Migration 036: Transceiver equivalences for competitor_verified matching -- Stores semantic equivalences between Flexoptix SKUs and competitor products -- matched by technical specs (form_factor + speed + reach + standard + fiber_type) CREATE TABLE IF NOT EXISTS transceiver_equivalences ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, flexoptix_id UUID NOT NULL REFERENCES transceivers(id) ON DELETE CASCADE, competitor_id UUID NOT NULL REFERENCES transceivers(id) ON DELETE CASCADE, confidence DECIMAL(4,3) NOT NULL CHECK (confidence BETWEEN 0 AND 1), match_basis TEXT[] NOT NULL DEFAULT '{}', -- ['standard_name','form_factor','speed_gbps','fiber_type','reach'] match_notes TEXT, status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','approved','rejected','auto_approved')), reviewed_by VARCHAR(200), reviewed_at TIMESTAMPTZ, reject_reason TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE (flexoptix_id, competitor_id) ); CREATE INDEX IF NOT EXISTS idx_eq_flexoptix ON transceiver_equivalences (flexoptix_id); CREATE INDEX IF NOT EXISTS idx_eq_competitor ON transceiver_equivalences (competitor_id); CREATE INDEX IF NOT EXISTS idx_eq_status ON transceiver_equivalences (status, confidence DESC); CREATE INDEX IF NOT EXISTS idx_eq_pending ON transceiver_equivalences (flexoptix_id) WHERE status = 'pending'; -- Auto-update updated_at CREATE OR REPLACE FUNCTION update_equivalences_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trg_eq_updated_at ON transceiver_equivalences; CREATE TRIGGER trg_eq_updated_at BEFORE UPDATE ON transceiver_equivalences FOR EACH ROW EXECUTE FUNCTION update_equivalences_updated_at(); COMMENT ON TABLE transceiver_equivalences IS 'Semantic equivalences between Flexoptix SKUs and competitor products, ' 'matched by technical specification overlap. Used to set competitor_verified=true ' 'on Flexoptix transceivers that have no exact SKU match at competitors.';