- @if ($.song.is_tie)
-
{{.song.scrobbles}} scrobbles ({{.song.rank}} place, tied)
- @else
{{.song.scrobbles}} scrobbles ({{.song.rank}} place)
- @end
-
All-time peak: {{.peak.rank}} ({{.peak.date}})
@partial partials/firstlast_listens(firstlast: .firstlast)
Yearly Performance
@partial partials/timescale(range: .yearly)
diff --git a/src/queries.zig b/src/queries.zig
index 778e64b..a665a58 100644
--- a/src/queries.zig
+++ b/src/queries.zig
@@ -3,43 +3,16 @@ const jetzig = @import("jetzig");
const TableRow = @import("types.zig").TableRow;
const HyperlinkData = @import("types.zig").HyperlinkData;
const std = @import("std");
-const ordinalFmt = @import("./ordinal_fmt.zig").ordinalFmt;
-pub fn entityQueryResult(request: *jetzig.Request, comptime query: GeneratedQuery, args: anytype) !*jetzig.Data.Value {
+pub fn entityQueryResult(request: *jetzig.Request, query: GeneratedQuery, args: anytype) !*jetzig.Data.Value {
//var result = try request.repo.executeSql(query.query, args);
//
- var Data = jetzig.Data.init(request.allocator);
-
- if (query.query_type == .peak) {
- const id = switch (@TypeOf(args)) {
- struct { i64 } => args[0],
- else => unreachable,
- };
- var result = try request.repo.connection.?.postgresql.connection.queryOpts(query.query, .{}, .{ .allocator = request.allocator, .column_names = true });
- var out: *jetzig.Data.Value = try Data.object();
- var mapper = result.mapper(PeakResult, .{ .dupe = true, .allocator = request.allocator });
- var rank = comptime (std.math.pow(u64, 2, 63) - 1);
- var date: []const u8 = undefined;
- var scrobbles = std.AutoArrayHashMap(i64, u32).init(request.allocator);
-
- while (try mapper.next()) |scrobble| {
- const res = try scrobbles.getOrPut(scrobble.eid);
- if (res.found_existing) res.value_ptr.* += 1 else res.value_ptr.* = 1;
- scrobbles.sort(PeakContext{ .keys = scrobbles.keys(), .vals = scrobbles.values(), .preferred = id });
- const idx = scrobbles.getIndex(id);
- if (idx != null and idx.? <= rank) {
- if (idx.? < rank) rank = idx.?;
- date = scrobble.datetime;
- }
- }
- try out.put("rank", ordinalFmt(request.allocator, @as(i64, @bitCast(rank + 1))));
- try out.put("date", date);
- return out;
- }
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) {
@@ -78,22 +51,7 @@ pub fn entityQueryResult(request: *jetzig.Request, comptime query: GeneratedQuer
}
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, friends, peak };
-
-const PeakResult = struct {
- eid: i64,
- datetime: []const u8,
-};
-
-const PeakContext = struct {
- keys: []i64,
- vals: []u32,
- preferred: i64,
- pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
- if (ctx.vals[a_index] == ctx.vals[b_index] and ctx.keys[a_index] == ctx.preferred) return true;
- return ctx.vals[a_index] > ctx.vals[b_index];
- }
-};
+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,
@@ -126,10 +84,9 @@ const EntityInfoResult = struct {
rank: []const u8,
song_num: ?i64 = null,
album_num: ?i64 = null,
- is_tie: bool,
};
-pub fn loadQuery(comptime entity: EntityType, comptime query_type: QueryTypeEnum) GeneratedQuery {
+pub fn loadQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQuery {
return GeneratedQuery{
.entity = entity,
.query_type = query_type,
@@ -387,49 +344,42 @@ pub fn loadQuery(comptime entity: EntityType, comptime query_type: QueryTypeEnum
},
.entity_info => switch (entity) {
- .scrobble => @compileError("Cannot specify scrobble for entity_info"),
+ .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;
+ \\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 =>
- \\WITH ranked AS (
- \\SELECT albums.name AS album_name, COUNT(albums.id) AS scrobbles, RANK() OVER ( ORDER BY COUNT(albums.id) DESC) AS rank, albums.id AS album_id, COUNT(DISTINCT songs.id) AS song_num
- \\FROM scrobbles
- \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
- \\INNER JOIN albums ON albums.id = albumsongs.album_id
+ \\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)
- \\SELECT * FROM (SELECT album_name, scrobbles, TO_CHAR(rank,'FM9999th') AS rank, album_id, song_num,
- \\( 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 album_id = $1;
+ \\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 =>
- \\WITH ranked AS (
- \\SELECT artists.name AS artist_name, COUNT(artists.id) AS scrobbles, RANK() OVER ( ORDER BY COUNT(artists.id) DESC) AS rank, artists.id AS artist_id, COUNT(DISTINCT songs.id) AS song_num, COUNT(DISTINCT albums.id) AS album_num
- \\FROM scrobbles
- \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
- \\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = scrobbles.albumsong
+ \\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
- \\INNER JOIN artists ON artists.id = albumsongsartists.artist_id
- \\GROUP BY artists.id)
- \\SELECT * FROM (SELECT artist_name, scrobbles, TO_CHAR(rank,'FM9999th') AS rank, artist_id, song_num, album_num,
- \\( 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 artist_id = $1;
+ \\GROUP BY artists.id))
+ \\WHERE artist_id = $1
,
},
@@ -504,45 +454,21 @@ pub fn loadQuery(comptime entity: EntityType, comptime query_type: QueryTypeEnum
,
else => unreachable,
},
- .friends => switch (entity) {
- .song =>
- \\SELECT name, COUNT(DISTINCT dt) AS days, COUNT(dt) AS plays
- \\FROM (
- \\ SELECT scrobbles.albumsong, songs.name, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD') AS dt
- \\ 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 days DESC, plays DESC, name ASC;
- ,
- else => unreachable,
- },
- .peak => switch (entity) {
- .scrobble => @compileError("Cannot specify scrobble for peak"),
- .song =>
- \\SELECT songs.id AS eid, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD') AS datetime FROM scrobbles
- \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
- \\INNER JOIN songs ON songs.id = albumsongs.song_id
- \\ORDER BY datetime ASC;
- ,
- .album =>
- \\SELECT albums.id AS eid, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD') AS datetime FROM scrobbles
- \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
- \\INNER JOIN albums ON albums.id = albumsongs.album_id
- \\ORDER BY datetime ASC;
- ,
- .artist =>
- \\SELECT artists.id AS eid, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD') AS datetime 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
- \\ORDER BY datetime ASC;
- ,
- },
},
};
}
+
+// 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;