Community engagement
city_battle_votes
Cross-city battle votes — the tier above per-city voting. Each row is one vote in a head-to-head between two cities' champion videos (the current month's highest-ELO approved video per city). Ranks CITIES, not videos, into the "Top dance cities" leaderboard. videoIds are stored for provenance only (plain uuid, no FK — champions rotate as ELO shifts, and this must survive video deletion).
Columns
| Column | Type | Null | Default | Keys |
|---|---|---|---|---|
| id | uuid | no | gen_random_uuid() | PK |
| competition_month | text | no | — | |
| winner_city_slug | text | no | — | |
| loser_city_slug | text | no | — | |
| winner_video_id | uuid | yes | — | |
| loser_video_id | uuid | yes | — | |
| voter_session_id | text | no | — | |
| voter_dancer_id | uuid | yes | — | |
| created_at | timestamp | yes | now() |
Value lists come from .$type<'a' | 'b'>() in the schema — enforced in TypeScript only, not by the database.
Keys, constraints & indexes
Unique
None besides the primary key.
Indexes
- (voter_session_id, competition_month)city_battle_votes_session_idx
- (competition_month)city_battle_votes_month_idx
Foreign keys
- voter_dancer_id → dancers.id on delete no action
Used by
Code that reads or writes this table, found by static analysis of the tRPC routers and API handlers (including helpers they call).
tRPC procedures
REST & webhooks
- ANY /mcp
- ANY /mcp/public
Relations
Rendering diagram…
Diagram source
erDiagram
city_battle_votes {
uuid id PK
uuid voter_dancer_id FK
}
dancers {
uuid id PK
text email UK
text neon_auth_id UK
text username UK
}
dancers |o--o{ city_battle_votes : "voter_dancer_id" Defined at server/database/schema.ts:211 as cityBattleVotes.