Files
stonks-oracle/infra/migrations/023_trend_windows_upsert_and_cleanup.sql
T
Celes Renata 86b549e5e1 fix: migrations preserve trend history across reinstalls
Migration 023 was deleting all but the latest trend_windows row per
entity before 024 could save them to trend_history. On reinstall,
this wiped the entire history every time.

Fixed by restructuring:
- 023 now creates trend_history FIRST and copies all trend_windows
  rows into it before deduplicating trend_windows down to latest-only.
  Uses NOT EXISTS to avoid duplicating rows on re-runs.
- 024 is now idempotent: ensures table/indexes exist and backfills
  from recommendations (last 7 days, 1 point per ticker/window/hour)
  to reconstruct approximate history even if trend_windows was sparse.

Both migrations are safe to re-run on existing databases.
2026-04-17 01:15:28 +00:00

66 lines
2.6 KiB
SQL

-- Fix trend_windows to upsert instead of accumulating rows.
-- First, preserve all historical data in trend_history before deduplicating.
-- Step 1: Create trend_history table if it doesn't exist yet
-- (migration 024 also creates it, but we need it here first to preserve data)
CREATE TABLE IF NOT EXISTS trend_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entity_type VARCHAR(50) NOT NULL DEFAULT 'company',
entity_id VARCHAR(100) NOT NULL,
"window" VARCHAR(20) NOT NULL,
trend_direction VARCHAR(20) NOT NULL DEFAULT 'neutral',
trend_strength FLOAT DEFAULT 0.5,
confidence FLOAT DEFAULT 0.5,
contradiction_score FLOAT DEFAULT 0.0,
dominant_catalysts JSONB DEFAULT '[]',
material_risks JSONB DEFAULT '[]',
generated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_trend_history_lookup
ON trend_history (entity_id, "window", generated_at DESC);
CREATE INDEX IF NOT EXISTS idx_trend_history_generated
ON trend_history (generated_at DESC);
-- Step 2: Copy ALL existing trend_windows rows into trend_history
-- before we delete any. This preserves the full history for charting.
INSERT INTO trend_history (
entity_type, entity_id, "window", trend_direction,
trend_strength, confidence, contradiction_score,
dominant_catalysts, material_risks, generated_at
)
SELECT
entity_type, entity_id, "window", trend_direction,
trend_strength, confidence, contradiction_score,
dominant_catalysts, material_risks, generated_at
FROM trend_windows
WHERE NOT EXISTS (
SELECT 1 FROM trend_history th
WHERE th.entity_id = trend_windows.entity_id
AND th."window" = trend_windows."window"
AND th.generated_at = trend_windows.generated_at
);
-- Step 3: Keep only the most recent row per (entity_type, entity_id, window)
-- in trend_windows (it becomes the "latest snapshot" table)
DELETE FROM trend_windows
WHERE id NOT IN (
SELECT DISTINCT ON (entity_type, entity_id, "window") id
FROM trend_windows
ORDER BY entity_type, entity_id, "window", generated_at DESC
);
-- Step 4: Add unique constraint for upsert
CREATE UNIQUE INDEX IF NOT EXISTS idx_trend_windows_entity_window
ON trend_windows (entity_type, entity_id, "window");
-- Step 5: Clean up old competitive signal records (keep last 30 days)
DELETE FROM competitive_signal_records
WHERE computed_at < NOW() - INTERVAL '30 days';
-- Step 6: Add a partial index to speed up the NOT EXISTS check in the
-- aggregation propagation query
CREATE INDEX IF NOT EXISTS idx_competitive_signals_source_doc_ticker
ON competitive_signal_records (source_document_id, source_ticker);