// 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 = if (entity.scrobbles) |scrobbles| scrobbles else null, .date = if (entity.date) |date| date else null, }); } 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 }; 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, }; 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, 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 \\GROUP BY songs.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 => \\SELECT * FROM \\(SELECT *, TO_CHAR(ROW_NUMBER() OVER (ORDER BY scrobbles DESC),'FM99999th') AS rank FROM \\(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 \\GROUP BY songs.id)) \\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, }, }, }; }