-- discgod schema (reference copy — the server applies this itself via -- discgod_ingest.ensure_schema() on startup). Additive: it reuses the existing -- marketwatcher mp_* tables verbatim and adds one watchlist table. -- -- Apply manually if you like: psql discogs_full -f schema.sql -- Sellers ever seen, with latest reputation stats. CREATE TABLE IF NOT EXISTS mp_seller ( seller_id BIGINT PRIMARY KEY, username TEXT NOT NULL, rating NUMERIC(3,1), rating_percent NUMERIC(5,2), ratings_count INTEGER, ships_from TEXT, first_seen TIMESTAMPTZ DEFAULT now(), last_seen TIMESTAMPTZ DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_mp_seller_username ON mp_seller (username); -- One row per marketplace listing (unique physical copy). release_id joins -- straight to your XML `release` table. CREATE TABLE IF NOT EXISTS mp_listing ( item_id BIGINT PRIMARY KEY, release_id BIGINT NOT NULL, seller_id BIGINT, seller_username TEXT, title TEXT, label TEXT, cat_no TEXT, media_condition TEXT, sleeve_condition TEXT, notes TEXT, price_value NUMERIC(12,2), price_currency TEXT, shipping_value NUMERIC(12,2), converted_value NUMERIC(12,2), have_count INTEGER, want_count INTEGER, ships_from TEXT, status TEXT NOT NULL DEFAULT 'active', -- active | gone gone_at TIMESTAMPTZ, first_seen TIMESTAMPTZ DEFAULT now(), last_seen TIMESTAMPTZ DEFAULT now(), times_seen INTEGER DEFAULT 1, last_crawl_id TEXT ); CREATE INDEX IF NOT EXISTS idx_mp_listing_release ON mp_listing (release_id); CREATE INDEX IF NOT EXISTS idx_mp_listing_seller ON mp_listing (seller_username, status); CREATE INDEX IF NOT EXISTS idx_mp_listing_seen ON mp_listing (last_seen DESC); -- Append-only state-change history per listing. -- event: listed | price_change | condition_change | gone | relisted CREATE TABLE IF NOT EXISTS mp_listing_event ( id BIGSERIAL PRIMARY KEY, item_id BIGINT NOT NULL, release_id BIGINT, seller_username TEXT, event TEXT NOT NULL, old_price NUMERIC(12,2), new_price NUMERIC(12,2), currency TEXT, details JSONB, recorded_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_mp_event_item ON mp_listing_event (item_id, recorded_at DESC); CREATE INDEX IF NOT EXISTS idx_mp_event_type ON mp_listing_event (event, recorded_at DESC); CREATE INDEX IF NOT EXISTS idx_mp_event_release ON mp_listing_event (release_id); -- Inventory-size time series per seller. source = 'api_inventory' for discgod -- (the authoritative pagination total from the Discogs API). CREATE TABLE IF NOT EXISTS mp_seller_snapshot ( id BIGSERIAL PRIMARY KEY, seller_id BIGINT, username TEXT NOT NULL, total_listings BIGINT, source TEXT NOT NULL, filter_key TEXT, filter_params JSONB, captured_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_mp_snapshot_series ON mp_seller_snapshot (username, source, filter_key, captured_at DESC); -- Provenance: every captured inventory page. CREATE TABLE IF NOT EXISTS mp_page_log ( id BIGSERIAL PRIMARY KEY, url TEXT, page_type TEXT, -- 'api_inventory' for discgod seller_page TEXT, params JSONB, page INTEGER, total_results BIGINT, item_count INTEGER, item_ids BIGINT[], new_items INTEGER, price_changes INTEGER, crawl_id TEXT, scraped_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_mp_page_log_time ON mp_page_log (scraped_at DESC); -- discgod watchlist: who to auto-crawl. Shared by the extension popup and the -- headless cron crawler. CREATE TABLE IF NOT EXISTS discgod_seller_watch ( username TEXT PRIMARY KEY, seller_id BIGINT, enabled BOOLEAN DEFAULT TRUE, note TEXT, filter_params JSONB, added_at TIMESTAMPTZ DEFAULT now(), last_crawled_at TIMESTAMPTZ, last_listing_count INTEGER, last_status TEXT ); CREATE INDEX IF NOT EXISTS idx_discgod_watch_enabled ON discgod_seller_watch (enabled);