The birthday paradox is a real problem with the size of our datasets. i64 is the largest numerical value we can use, and there's a 0.1% chance of collision with ~2,000,000 values, so I feel pretty comfortable with this
424 lines
21 KiB
Zig
424 lines
21 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 = 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,
|
|
},
|
|
},
|
|
};
|
|
}
|