zuletzt/src/queries.zig

477 lines
24 KiB
Zig

// Probably the worst code in the project
const jetzig = @import("jetzig");
const TableRow = @import("types.zig").TableRow;
const HyperlinkData = @import("types.zig").HyperlinkData;
const std = @import("std");
pub fn entityQueryResult(request: *jetzig.Request, query: GeneratedQuery, args: anytype) !*jetzig.Data.Value {
//var result = try request.repo.executeSql(query.query, args);
//
var result = try request.repo.connection.?.postgresql.connection.queryOpts(query.query, args, .{ .allocator = request.allocator, .column_names = true });
defer result.deinit();
var Data = jetzig.Data.init(request.allocator);
var artist_list = std.ArrayList(HyperlinkData).init(request.allocator);
if (query.query_type == .entity_info) {
var out: *jetzig.Data.Value = try Data.object();
const entity = try (try result.next()).?.to(EntityInfoResult, .{ .dupe = true, .allocator = request.allocator, .map = .name });
try out.put("entity_info", entity);
try result.drain();
return out.get("entity_info").?;
}
var out: *jetzig.Data.Value = try Data.array();
var mapper = result.mapper(UnifiedResult, .{ .dupe = true, .allocator = request.allocator });
blk: while (try mapper.next()) |entity| {
if (entity.artist_id) |_| {
const last_artist = artist_list.getLastOrNull();
try artist_list.append(.{ .name = entity.artist_name.?, .id = entity.artist_id.? });
if (last_artist) |la| {
if (la.id == entity.artist_id) continue :blk;
}
}
try out.append(TableRow{
.artist = if (entity.artist_id) |_| .{ .id = entity.artist_id.?, .name = entity.artist_name.? } else null,
.album = if (entity.album_id) |_| .{ .id = entity.album_id.?, .name = entity.album_name.? } else null,
.song = if (entity.song_id) |_| .{ .id = entity.song_id.?, .name = entity.song_name.? } else null,
.artistlist = if (artist_list.getLastOrNull()) |_| try artist_list.toOwnedSlice() else null,
.scrobbles = entity.scrobbles,
.date = entity.date,
.score = entity.score,
.review = entity.review,
});
}
return out;
}
const EntityType = enum { scrobble, song, album, artist };
const QueryTypeEnum = enum { firstlast, timescale, entities, get_songs, get_albums, get_scrobbles, appears, entity_info, datestreak, entities_by_name, get_ratings };
const GeneratedQuery = struct {
entity: EntityType,
query_type: QueryTypeEnum,
query: []const u8,
};
const UnifiedResult = struct {
album_name: ?[]const u8 = null,
album_id: ?i64 = null,
song_name: ?[]const u8 = null,
song_id: ?i64 = null,
artist_name: ?[]const u8 = null,
artist_id: ?i64 = null,
scrobbles: ?i64 = null,
date: ?[]const u8 = null,
score: ?i16 = null,
review: ?[]const u8 = null,
};
const EntityInfoResult = struct {
album_name: ?[]const u8 = null,
album_id: ?i64 = null,
song_name: ?[]const u8 = null,
song_id: ?i64 = null,
artist_name: ?[]const u8 = null,
artist_id: ?i64 = null,
scrobbles: ?i64 = null,
date: ?[]const u8 = null,
rank: []const u8,
song_num: ?i64 = null,
album_num: ?i64 = null,
};
pub fn loadQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQuery {
return GeneratedQuery{
.entity = entity,
.query_type = query_type,
.query = switch (query_type) {
.firstlast =>
//.ResultType = FirstlastResult,
switch (entity) {
.scrobble => unreachable,
.song =>
\\(SELECT songs.name AS song_name, songs.id AS song_id, TO_CHAR(scrobbles.datetime,'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
\\WHERE albumsongs.song_id = $1
\\ORDER BY scrobbles.datetime ASC
\\LIMIT 1)
\\
\\UNION ALL
\\
\\(SELECT songs.name AS song_name, songs.id AS song_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
\\WHERE albumsongs.song_id = $1
\\ORDER BY scrobbles.datetime DESC
\\LIMIT 1)
,
.album =>
\\(SELECT songs.name AS song_name, songs.id AS song_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\WHERE albums.id = $1
\\ORDER BY scrobbles.datetime ASC
\\LIMIT 1)
\\
\\UNION ALL
\\
\\(SELECT songs.name AS song_name, songs.id AS song_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\WHERE albums.id = $1
\\ORDER BY scrobbles.datetime DESC
\\LIMIT 1)
,
.artist =>
\\(SELECT songs.name AS song_name, songs.id AS song_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\WHERE albumsongsartists.artist_id = $1
\\ORDER BY scrobbles.datetime ASC
\\LIMIT 1)
\\
\\UNION ALL
\\
\\(SELECT songs.name AS song_name, songs.id AS song_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\WHERE albumsongsartists.artist_id = $1
\\ORDER BY scrobbles.datetime DESC
\\LIMIT 1)
,
},
.timescale =>
//.ResultType = TimescaleResult,
switch (entity) {
.scrobble => unreachable,
.song =>
\\SELECT TO_CHAR(date_trunc('year', datetime), 'YYYY') AS date, COUNT(*) as scrobbles
\\FROM scrobbles
\\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\WHERE songs.id = $1
\\GROUP BY date
\\ORDER BY date ASC;
,
.album =>
\\SELECT TO_CHAR(date_trunc('year', datetime), 'YYYY') AS date, COUNT(*) as scrobbles
\\FROM scrobbles
\\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\WHERE albums.id = $1
\\GROUP BY date
\\ORDER BY date ASC;
,
.artist =>
\\SELECT y.year AS date, COALESCE(DT.scrobbles, 0) AS scrobbles
\\FROM (SELECT GENERATE_SERIES(2016,date_part('year',now())::int)::text) AS y(year)
\\LEFT JOIN (SELECT TO_CHAR(date_trunc('year', datetime), 'YYYY') AS date, COUNT(*) as scrobbles
\\FROM scrobbles
\\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\INNER JOIN artists ON artists.id = albumsongsartists.artist_id
\\WHERE artists.id = $1
\\GROUP BY date
\\ORDER BY date ASC) AS DT
\\ON DT.date = y.year;
,
},
.entities =>
//.ResultType = EntitiesResult,
switch (entity) {
.scrobble =>
\\SELECT songs.name AS song_name, songs.id AS song_id, albums.name AS album_name, albums.id AS album_id, artists.name AS artist_name, artists.id AS artist_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\INNER JOIN artists ON artists.id = albumsongsartists.artist_id
\\ORDER BY scrobbles.datetime ASC
,
.song =>
\\SELECT songs.name AS song_name, songs.id AS song_id, albums.name AS album_name, albums.id AS album_id, artists.name AS artist_name, artists.id AS artist_id, COUNT(scrobbles) AS scrobbles
\\FROM albumsongs
\\INNER JOIN songs ON albumsongs.song_id = songs.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\INNER JOIN artists ON artists.id = albumsongsartists.artist_id
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\GROUP BY songs.id, albums.id, artists.id
\\ORDER BY scrobbles DESC, songs.name ASC
,
.album =>
\\SELECT albums.name AS album_name, albums.id AS album_id, artists.name AS artist_name, artists.id AS artist_id, COUNT(scrobbles) AS scrobbles
\\FROM albumsongs
\\INNER JOIN albums ON albumsongs.album_id = albums.id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN artistalbums ON artistalbums.album_id = albums.id
\\INNER JOIN artists ON artists.id = artistalbums.artist_id
\\GROUP BY albums.id, artists.id
\\ORDER BY scrobbles DESC
,
.artist =>
\\SELECT artists.name AS artist_name, artists.id AS artist_id, COUNT(scrobbles) AS scrobbles
\\FROM albumsongsartists
\\INNER JOIN artists ON albumsongsartists.artist_id = artists.id
\\INNER JOIN albumsongs ON albumsongsartists.albumsong_id = albumsongs.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\GROUP BY artists.id
\\ORDER BY scrobbles DESC;
,
},
.appears =>
// Not sure how I feel about this one
switch (entity) {
.scrobble, .song, .album => unreachable,
.artist =>
\\SELECT albums.name AS album_name, albums.id AS album_id, COUNT(scrobbles) AS scrobbles
\\FROM artistalbums
\\INNER JOIN albums ON albums.id = artistalbums.album_id
\\INNER JOIN albumsongs ON albumsongs.album_id = albums.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\WHERE albumsongsartists.artist_id = $1 AND artistalbums.artist_id != $1
\\GROUP BY albums.id
\\ORDER BY scrobbles DESC;
,
},
.get_songs => switch (entity) {
.scrobble, .song => unreachable, // Might be able to use this with SongGroups?
.album =>
\\SELECT songs.name AS song_name, songs.id AS song_id, COUNT(scrobbles) AS scrobbles
\\FROM albumsongs
\\INNER JOIN songs ON albumsongs.song_id = songs.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\WHERE albumsongs.album_id = $1
\\GROUP BY songs.id
\\ORDER BY scrobbles DESC
,
.artist =>
\\SELECT songs.name AS song_name, songs.id AS song_id, albums.name AS album_name, albums.id AS album_id, COUNT(scrobbles) AS scrobbles
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\WHERE albumsongsartists.artist_id = $1
\\GROUP BY songs.id, albums.id
\\ORDER BY scrobbles DESC
,
},
.get_albums =>
//.ResultType = EntityItemsResult,
switch (entity) {
.scrobble, .album => unreachable, // Might be able to use this with ReleaseGroups?
.song =>
\\SELECT albums.name AS album_name, albums.id AS album_id, COUNT(scrobbles) AS scrobbles
\\FROM artistalbums
\\INNER JOIN albums ON albums.id = artistalbums.album_id
\\INNER JOIN albumsongs ON albumsongs.album_id = albums.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\WHERE albumsongs.song_id = $1
\\GROUP BY albums.id
\\ORDER BY scrobbles DESC
,
.artist =>
\\SELECT albums.name AS album_name, albums.id AS album_id, COUNT(scrobbles) AS scrobbles
\\FROM artistalbums
\\INNER JOIN albums ON albums.id = artistalbums.album_id
\\INNER JOIN albumsongs ON albumsongs.album_id = albums.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\WHERE artistalbums.artist_id = $1
\\GROUP BY albums.id
\\ORDER BY scrobbles DESC
,
},
.get_scrobbles => switch (entity) {
.scrobble => unreachable,
.song =>
\\SELECT songs.name AS song_name, songs.id AS song_id, albums.name AS album_name, albums.id AS album_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\WHERE songs.id = $1
\\ORDER BY date ASC
,
.album =>
\\SELECT songs.name AS song_name, songs.id AS song_id, albums.name AS album_name, albums.id AS album_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\WHERE albums.id = $1
\\ORDER BY date ASC
,
.artist =>
\\SELECT songs.name AS song_name, songs.id AS song_id, albums.name AS album_name, albums.id AS album_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\WHERE artists.id = $1
\\ORDER BY date ASC
,
},
.entity_info => switch (entity) {
.scrobble => unreachable,
.song =>
\\WITH ranked AS (
\\SELECT songs.name AS song_name, COUNT(songs.id) AS scrobbles, RANK() OVER ( ORDER BY COUNT(songs.id) DESC) AS rank, songs.id AS song_id
\\FROM scrobbles
\\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\GROUP BY songs.id)
\\SELECT * FROM (SELECT song_name, scrobbles, TO_CHAR(rank,'FM9999th') AS rank, song_id,
\\( rank = lag(rank, 1, -1::bigint) OVER (ORDER BY rank)
\\OR rank = lead(rank, 1, -1::bigint) OVER (ORDER BY rank)
\\)AS is_tie
\\FROM ranked) WHERE song_id = $1;
,
.album =>
\\SELECT album_name, t.album_id, artists.name AS artist_name, artists.id AS artist_id, song_num, scrobbles, rank FROM
\\(SELECT *, TO_CHAR(ROW_NUMBER() OVER (ORDER BY scrobbles DESC),'FM9999th') AS rank FROM
\\(SELECT albums.name AS album_name, albums.id AS album_id, COUNT(DISTINCT songs.id) AS song_num, COUNT(scrobbles) AS scrobbles
\\FROM albumsongs
\\INNER JOIN albums ON albumsongs.album_id = albums.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\GROUP BY albums.id)) AS t
\\INNER JOIN artistalbums ON artistalbums.album_id = t.album_id
\\INNER JOIN artists ON artists.id = artistalbums.artist_id
\\WHERE t.album_id = $1
,
.artist =>
\\SELECT * FROM
\\(SELECT *, TO_CHAR(ROW_NUMBER() OVER (ORDER BY scrobbles DESC),'FM9999th') AS rank FROM
\\(SELECT artists.name AS artist_name, artists.id AS artist_id, COUNT(scrobbles) AS scrobbles, COUNT(DISTINCT albums.id) AS album_num, COUNT(DISTINCT songs.id) AS song_num
\\FROM albumsongsartists
\\INNER JOIN artists ON albumsongsartists.artist_id = artists.id
\\INNER JOIN albumsongs ON albumsongsartists.albumsong_id = albumsongs.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\GROUP BY artists.id))
\\WHERE artist_id = $1
,
},
.datestreak => switch (entity) {
.song =>
\\SELECT maxseq AS streak, FORMAT('%s - %s', ds, de) AS date FROM (SELECT MAX(numdays) AS maxseq, ds, de
\\FROM (SELECT grp, MIN(datetime::date) AS ds, MAX(datetime::date) AS de,
\\COUNT(DISTINCT datetime::date) AS numdays
\\FROM (SELECT scrobbles.datetime,
\\((datetime::date - '1970-01-01'::date) - DENSE_RANK() OVER (PARTITION BY songs.id ORDER BY datetime::date)) AS grp
\\FROM scrobbles INNER JOIN albumsongs ON albumsongs.id = albumsong INNER JOIN songs ON songs.id = albumsongs.song_id
\\WHERE songs.id = $1) scrobbles
\\GROUP BY grp
\\) scrobbles
\\GROUP BY ds, de)
\\ORDER BY maxseq DESC;
,
.artist =>
\\SELECT maxseq AS streak, FORMAT('%s - %s' ,ds,de) AS date FROM (SELECT MAX(numdays) AS maxseq, ds, de
\\FROM (SELECT grp, MIN(datetime::date) AS ds, MAX(datetime::date) AS de,
\\COUNT(DISTINCT datetime::date) AS numdays
\\FROM (SELECT scrobbles.datetime,
\\((scrobbles.datetime::date - '1970-01-01'::date) - DENSE_RANK() OVER (PARTITION BY artists.id ORDER BY scrobbles.datetime::date)) AS grp
\\FROM scrobbles INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = scrobbles.albumsong INNER JOIN artists ON artists.id = albumsongsartists.artist_id
\\WHERE artists.id = $1) scrobbles
\\GROUP BY grp
\\) scrobbles
\\GROUP BY ds, de)
\\ORDER BY maxseq DESC;
,
.album =>
\\SELECT maxseq AS streak, FORMAT('%s - %s', ds, de) AS date FROM (SELECT MAX(numdays) AS maxseq, ds, de
\\FROM (SELECT grp, MIN(datetime::date) AS ds, MAX(datetime::date) AS de,
\\COUNT(DISTINCT datetime::date) AS numdays
\\FROM (SELECT scrobbles.datetime,
\\((datetime::date - '1970-01-01'::date) - DENSE_RANK() OVER (PARTITION BY albums.id ORDER BY datetime::date)) AS grp FROM scrobbles INNER JOIN albumsongs ON albumsongs.id = albumsong INNER JOIN albums ON albums.id = albumsongs.album_id
\\WHERE albums.id = $1) scrobbles
\\GROUP BY grp
\\) scrobbles
\\GROUP BY ds, de)
\\ORDER BY maxseq DESC;
,
.scrobble => unreachable,
},
.entities_by_name => switch (entity) {
.song =>
\\SELECT songs.name AS song_name, songs.id AS song_id, albums.name AS album_name, albums.id AS album_id, artists.name AS artist_name, artists.id AS artist_id, COUNT(scrobbles) AS scrobbles
\\FROM albumsongs
\\INNER JOIN songs ON albumsongs.song_id = songs.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\INNER JOIN artists ON artists.id = albumsongsartists.artist_id
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\WHERE LOWER(songs.name) LIKE LOWER($1)
\\GROUP BY songs.id, albums.id, artists.id
\\ORDER BY songs.name ASC, scrobbles DESC;
,
else => unreachable,
},
.get_ratings => switch (entity) {
.song =>
\\SELECT rating AS score, rating_text AS review, TO_CHAR(date, 'YYYY-MM-DD') AS date
\\FROM songratings
\\WHERE song = $1
\\ORDER BY date DESC;
,
.album =>
\\SELECT rating AS score, rating_text AS review, TO_CHAR(date, 'YYY-MM-DD') AS date
\\FROM albumratings
\\WHERE album = $1
\\ORDER BY date DESC;
,
else => unreachable,
},
},
};
}
// I'm pretty sure this query will tell you the number of days a song was played on the same day as a specified song
// The output looked right at least. Can easily be changed into the number of times a song was played on the same day
// as a specified song by removing DISTINCT from the first subquery (which would increase the count if a song was
// played more than once in a day)
//SELECT COUNT(albumsong), name
//FROM (
// SELECT DISTINCT scrobbles.albumsong, songs.name, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD')
// FROM scrobbles
// INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
// INNER JOIN songs ON songs.id = albumsongs.song_id
// WHERE TO_CHAR(datetime,'YYYY-MM-DD') IN (
// SELECT DISTINCT TO_CHAR(datetime,'YYYY-MM-DD') FROM scrobbles WHERE albumsong = $1
//)) GROUP BY albumsong, name ORDER BY COUNT(albumsong) DESC, name ASC;