feat(migrations): catalog measurements read functions (aggregate + paginated page)

Merged
SW
4mo ago
+74-0

Backs the data plumbing v2 amendment of recoupable/chat#1850 (decision 2026-07-07: whole-scope aggregates via a single SQL aggregate; app-code loop-to-exhaustion rejected).

What

One migration adding two STABLE sql functions (RPC precedent: claim_songstats_backfill_rows, deduct_credits_with_audit):

  • get_catalog_measurements_aggregate(p_catalog uuid, p_artist uuid default null) → (measured_song_count bigint, total_streams numeric): count + sum over the latest-per-ISRC (DISTINCT ON) subquery for the catalog's songs, optionally restricted to those linked to the artist account via song_artists.
  • get_catalog_measurements_page(p_catalog uuid, p_artist uuid default null, p_limit int default 50, p_offset int default 0) → latest-per-ISRC rows (isrc, title, playcount, measured_at) sorted by playcount desc, for the caller-paginated measurements array.

DISTINCT ON is not expressible through supabase-js, so both reads live here.

Status

Already applied to the shared project (via MCP, schema_migrations.version aligned to 20260707120000) so the api PR preview can verify. Verified against the [TEST] Full Roster Catalog (aggregate) 7d3e5c35…:

Scopemeasured_song_counttotal_streams
whole catalog2,67916,308,837,441
Elvis Crespo b1814076…3221,799,402,184
September Mourning d85f9606…4021,325,663
Apache ebae4bb9…208742,453,110
J Alvarez 6f1d797f…6086,717,937,120

All match the independent read-only SQL ground truth exactly.

Merge order

recoupable/docs#267 (contract) → this migration → recoupable/api#763 (implementation) → chat#1852. Refs recoupable/chat#1850.

🤖 Generated with Claude Code


Summary by cubic

Adds two STABLE SQL RPCs to power GET /api/catalogs/measurements v2: a whole-scope aggregate and a paginated latest-per-ISRC page. This delivers accurate totals and ranked rows in one query, avoiding app-side loops and supabase-js limits.

  • New Features
    • get_catalog_measurements_aggregate(p_catalog, p_artist?): returns measured_song_count and total_streams across the latest-per-ISRC Spotify playcount; optional artist filter.
    • get_catalog_measurements_page(p_catalog, p_artist?, p_limit=50, p_offset=0): returns latest-per-ISRC rows (isrc, title, playcount, measured_at) sorted by playcount desc; paginated; optional artist filter.