1CREATE TABLE artists (
2 mbid UUID PRIMARY KEY,
3 name TEXT NOT NULL,
4 play_count INTEGER DEFAULT 0
5);
6
7-- releases are synologous to 'albums'
8CREATE TABLE releases (
9 mbid UUID PRIMARY KEY,
10 name TEXT NOT NULL,
11 play_count INTEGER DEFAULT 0
12);
13
14-- recordings are synologous to 'tracks' BUT tracks can be in multiple releases!
15CREATE TABLE recordings (
16 mbid UUID PRIMARY KEY,
17 name TEXT NOT NULL,
18 play_count INTEGER DEFAULT 0
19);
20
21CREATE TABLE plays (
22 uri TEXT PRIMARY KEY,
23 did TEXT NOT NULL,
24 rkey TEXT NOT NULL,
25 cid TEXT NOT NULL,
26 isrc TEXT,
27 duration INTEGER,
28 track_name TEXT NOT NULL,
29 played_time TIMESTAMP WITH TIME ZONE,
30 processed_time TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
31 release_mbid UUID,
32 release_name TEXT,
33 recording_mbid UUID,
34 submission_client_agent TEXT,
35 music_service_base_domain TEXT,
36 origin_url TEXT,
37 FOREIGN KEY (release_mbid) REFERENCES releases (mbid),
38 FOREIGN KEY (recording_mbid) REFERENCES recordings (mbid)
39);
40
41CREATE INDEX idx_plays_release_mbid ON plays (release_mbid);
42
43CREATE INDEX idx_plays_recording_mbid ON plays (recording_mbid);
44
45CREATE INDEX idx_plays_played_time ON plays (played_time);
46
47CREATE INDEX idx_plays_did ON plays (did);
48
49CREATE TABLE play_to_artists (
50 play_uri TEXT, -- references plays(uri)
51 artist_mbid UUID REFERENCES artists (mbid),
52 artist_name TEXT, -- storing here for ease of use when joining
53 PRIMARY KEY (play_uri, artist_mbid),
54 FOREIGN KEY (play_uri) REFERENCES plays (uri)
55);
56
57CREATE INDEX idx_play_to_artists_artist ON play_to_artists (artist_mbid);
58
59-- Profiles table
60CREATE TABLE profiles (
61 did TEXT PRIMARY KEY,
62 handle TEXT,
63 display_name TEXT,
64 description TEXT,
65 description_facets JSONB,
66 avatar TEXT, -- IPLD of the image, bafy...
67 banner TEXT,
68 created_at TIMESTAMP WITH TIME ZONE
69);
70
71-- User featured items table
72CREATE TABLE featured_items (
73 did TEXT PRIMARY KEY,
74 mbid TEXT NOT NULL,
75 type TEXT NOT NULL
76);
77
78-- Statii table (status records)
79CREATE TABLE statii (
80 uri TEXT PRIMARY KEY,
81 did TEXT NOT NULL,
82 rkey TEXT NOT NULL,
83 cid TEXT NOT NULL,
84 record JSONB NOT NULL,
85 indexed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
86);
87
88CREATE INDEX idx_statii_did_rkey ON statii (did, rkey);
89
90-- Materialized view for artists' play counts
91CREATE MATERIALIZED VIEW mv_artist_play_counts AS
92SELECT
93 a.mbid AS artist_mbid,
94 a.name AS artist_name,
95 COUNT(p.uri) AS play_count
96FROM
97 artists a
98 LEFT JOIN play_to_artists pta ON a.mbid = pta.artist_mbid
99 LEFT JOIN plays p ON p.uri = pta.play_uri
100GROUP BY
101 a.mbid,
102 a.name;
103
104CREATE UNIQUE INDEX idx_mv_artist_play_counts ON mv_artist_play_counts (artist_mbid);
105
106-- Materialized view for releases' play counts
107CREATE MATERIALIZED VIEW mv_release_play_counts AS
108SELECT
109 r.mbid AS release_mbid,
110 r.name AS release_name,
111 COUNT(p.uri) AS play_count
112FROM
113 releases r
114 LEFT JOIN plays p ON p.release_mbid = r.mbid
115GROUP BY
116 r.mbid,
117 r.name;
118
119CREATE UNIQUE INDEX idx_mv_release_play_counts ON mv_release_play_counts (release_mbid);
120
121-- Materialized view for recordings' play counts
122CREATE MATERIALIZED VIEW mv_recording_play_counts AS
123SELECT
124 rec.mbid AS recording_mbid,
125 rec.name AS recording_name,
126 COUNT(p.uri) AS play_count
127FROM
128 recordings rec
129 LEFT JOIN plays p ON p.recording_mbid = rec.mbid
130GROUP BY
131 rec.mbid,
132 rec.name;
133
134CREATE UNIQUE INDEX idx_mv_recording_play_counts ON mv_recording_play_counts (recording_mbid);
135
136-- Global play count materialized view
137CREATE MATERIALIZED VIEW mv_global_play_count AS
138SELECT
139 COUNT(uri) AS total_plays,
140 COUNT(DISTINCT did) AS unique_listeners
141FROM plays;
142
143CREATE UNIQUE INDEX idx_mv_global_play_count ON mv_global_play_count(total_plays);
144
145-- Top artists in the last 30 days
146CREATE MATERIALIZED VIEW mv_top_artists_30days AS
147SELECT
148 a.mbid AS artist_mbid,
149 a.name AS artist_name,
150 COUNT(p.uri) AS play_count
151FROM artists a
152INNER JOIN play_to_artists pta ON a.mbid = pta.artist_mbid
153INNER JOIN plays p ON p.uri = pta.play_uri
154WHERE p.played_time >= NOW() - INTERVAL '30 days'
155GROUP BY a.mbid, a.name
156ORDER BY COUNT(p.uri) DESC;
157
158-- Top releases in the last 30 days
159CREATE MATERIALIZED VIEW mv_top_releases_30days AS
160SELECT
161 r.mbid AS release_mbid,
162 r.name AS release_name,
163 COUNT(p.uri) AS play_count
164FROM releases r
165INNER JOIN plays p ON p.release_mbid = r.mbid
166WHERE p.played_time >= NOW() - INTERVAL '30 days'
167GROUP BY r.mbid, r.name
168ORDER BY COUNT(p.uri) DESC;
169
170-- Top artists for user in the last 30 days
171CREATE MATERIALIZED VIEW mv_top_artists_for_user_30days AS
172SELECT
173 prof.did,
174 a.mbid AS artist_mbid,
175 a.name AS artist_name,
176 COUNT(p.uri) AS play_count
177FROM artists a
178INNER JOIN play_to_artists pta ON a.mbid = pta.artist_mbid
179INNER JOIN plays p ON p.uri = pta.play_uri
180INNER JOIN profiles prof ON prof.did = p.did
181WHERE p.played_time >= NOW() - INTERVAL '30 days'
182GROUP BY prof.did, a.mbid, a.name
183ORDER BY COUNT(p.uri) DESC;
184
185-- Top artists for user in the last 7 days
186CREATE MATERIALIZED VIEW mv_top_artists_for_user_7days AS
187SELECT
188 prof.did,
189 a.mbid AS artist_mbid,
190 a.name AS artist_name,
191 COUNT(p.uri) AS play_count
192FROM artists a
193INNER JOIN play_to_artists pta ON a.mbid = pta.artist_mbid
194INNER JOIN plays p ON p.uri = pta.play_uri
195INNER JOIN profiles prof ON prof.did = p.did
196WHERE p.played_time >= NOW() - INTERVAL '7 days'
197GROUP BY prof.did, a.mbid, a.name
198ORDER BY COUNT(p.uri) DESC;
199
200-- Top releases for user in the last 30 days
201CREATE MATERIALIZED VIEW mv_top_releases_for_user_30days AS
202SELECT
203 prof.did,
204 r.mbid AS release_mbid,
205 r.name AS release_name,
206 COUNT(p.uri) AS play_count
207FROM releases r
208INNER JOIN plays p ON p.release_mbid = r.mbid
209INNER JOIN profiles prof ON prof.did = p.did
210WHERE p.played_time >= NOW() - INTERVAL '30 days'
211GROUP BY prof.did, r.mbid, r.name
212ORDER BY COUNT(p.uri) DESC;
213
214-- Top releases for user in the last 7 days
215CREATE MATERIALIZED VIEW mv_top_releases_for_user_7days AS
216SELECT
217 prof.did,
218 r.mbid AS release_mbid,
219 r.name AS release_name,
220 COUNT(p.uri) AS play_count
221FROM releases r
222INNER JOIN plays p ON p.release_mbid = r.mbid
223INNER JOIN profiles prof ON prof.did = p.did
224WHERE p.played_time >= NOW() - INTERVAL '7 days'
225GROUP BY prof.did, r.mbid, r.name
226ORDER BY COUNT(p.uri) DESC;