Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor size on disk calculation for track files #4549

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/NzbDrone.Core.Test/ArtistStatsTests/ArtistStatisticsFixture.cs
Expand Up @@ -3,6 +3,7 @@
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.ArtistStats;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Music;
Expand Down Expand Up @@ -124,5 +125,26 @@ public void should_have_size_on_disk_when_track_file_exists()
stats.Should().HaveCount(1);
stats.First().SizeOnDisk.Should().Be(_trackFile.Size);
}

[Test]
public void should_not_duplicate_size_for_multi_track_files()
{
GivenTrackWithFile();
GivenTrack();
GivenTrackFile();

var track2 = _track.JsonClone();

track2.Id = 0;
track2.TrackNumber += 1;
track2.ForeignTrackId = "2";

Db.Insert(track2);

var stats = Subject.ArtistStatistics();

stats.Should().HaveCount(1);
stats.First().SizeOnDisk.Should().Be(_trackFile.Size);
}
}
}
59 changes: 37 additions & 22 deletions src/NzbDrone.Core/ArtistStats/ArtistStatisticsRepository.cs
Expand Up @@ -16,7 +16,8 @@ public interface IArtistStatisticsRepository

public class ArtistStatisticsRepository : IArtistStatisticsRepository
{
private const string _selectTemplate = "SELECT /**select**/ FROM \"Tracks\" /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/ /**orderby**/";
private const string _selectTracksTemplate = "SELECT /**select**/ FROM \"Tracks\" /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/ /**orderby**/";
private const string _selectTrackFilesTemplate = "SELECT /**select**/ FROM \"TrackFiles\" /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/ /**orderby**/";

private readonly IMainDatabase _database;

Expand All @@ -28,58 +29,72 @@ public ArtistStatisticsRepository(IMainDatabase database)
public List<AlbumStatistics> ArtistStatistics()
{
var time = DateTime.UtcNow;

if (_database.DatabaseType == DatabaseType.PostgreSQL)
{
return Query(Builder().WherePostgres<Album>(x => x.ReleaseDate < time));
}

return Query(Builder().Where<Album>(x => x.ReleaseDate < time));
return MapResults(Query(TracksBuilder(time), _selectTracksTemplate),
Query(TrackFilesBuilder(), _selectTrackFilesTemplate));
}

public List<AlbumStatistics> ArtistStatistics(int artistId)
{
var time = DateTime.UtcNow;

if (_database.DatabaseType == DatabaseType.PostgreSQL)
return MapResults(Query(TracksBuilder(time).Where<Track>(x => x.ArtistMetadataId == artistId), _selectTracksTemplate),
Query(TrackFilesBuilder().Where<TrackFile>(x => x.Album.Value.ArtistMetadataId == artistId), _selectTrackFilesTemplate));
}

private List<AlbumStatistics> MapResults(List<AlbumStatistics> tracksResult, List<AlbumStatistics> filesResult)
{
tracksResult.ForEach(e =>
{
return Query(Builder().WherePostgres<Album>(x => x.ReleaseDate < time)
.WherePostgres<Artist>(x => x.Id == artistId));
}
var file = filesResult.SingleOrDefault(f => f.ArtistId == e.ArtistId & f.AlbumId == e.AlbumId);

return Query(Builder().Where<Album>(x => x.ReleaseDate < time)
.Where<Artist>(x => x.Id == artistId));
e.SizeOnDisk = file?.SizeOnDisk ?? 0;
});

return tracksResult;
}

private List<AlbumStatistics> Query(SqlBuilder builder)
private List<AlbumStatistics> Query(SqlBuilder builder, string template)
{
var sql = builder.AddTemplate(_selectTemplate).LogQuery();
var sql = builder.AddTemplate(template).LogQuery();

using (var conn = _database.OpenConnection())
{
return conn.Query<AlbumStatistics>(sql.RawSql, sql.Parameters).ToList();
}
}

private SqlBuilder Builder()
private SqlBuilder TracksBuilder(DateTime currentDate)
{
var parameters = new DynamicParameters();
parameters.Add("currentDate", currentDate, null);

var trueIndicator = _database.DatabaseType == DatabaseType.PostgreSQL ? "true" : "1";

return new SqlBuilder(_database.DatabaseType)
.Select($@"""Artists"".""Id"" AS ""ArtistId"",
""Albums"".""Id"" AS ""AlbumId"",
SUM(COALESCE(""TrackFiles"".""Size"", 0)) AS ""SizeOnDisk"",
COUNT(""Tracks"".""Id"") AS ""TotalTrackCount"",
SUM(CASE WHEN ""Tracks"".""TrackFileId"" > 0 THEN 1 ELSE 0 END) AS ""AvailableTrackCount"",
SUM(CASE WHEN ""Albums"".""Monitored"" = {trueIndicator} OR ""Tracks"".""TrackFileId"" > 0 THEN 1 ELSE 0 END) AS ""TrackCount"",
SUM(CASE WHEN ""TrackFiles"".""Id"" IS NULL THEN 0 ELSE 1 END) AS ""TrackFileCount""")
SUM(CASE WHEN ""Albums"".""ReleaseDate"" <= @currentDate OR ""Tracks"".""TrackFileId"" > 0 THEN 1 ELSE 0 END) AS ""AvailableTrackCount"",
SUM(CASE WHEN (""Albums"".""Monitored"" = {trueIndicator} AND ""Albums"".""ReleaseDate"" <= @currentDate) OR ""Tracks"".""TrackFileId"" > 0 THEN 1 ELSE 0 END) AS ""TrackCount"",
SUM(CASE WHEN ""Tracks"".""TrackFileId"" > 0 THEN 1 ELSE 0 END) AS TrackFileCount", parameters)
.Join<Track, AlbumRelease>((t, r) => t.AlbumReleaseId == r.Id)
.Join<AlbumRelease, Album>((r, a) => r.AlbumId == a.Id)
.Join<Album, Artist>((album, artist) => album.ArtistMetadataId == artist.ArtistMetadataId)
.LeftJoin<Track, TrackFile>((t, f) => t.TrackFileId == f.Id)
.Where<AlbumRelease>(x => x.Monitored == true)
.GroupBy<Artist>(x => x.Id)
.GroupBy<Album>(x => x.Id);
}

private SqlBuilder TrackFilesBuilder()
{
return new SqlBuilder(_database.DatabaseType)
.Select(@"""Artists"".""Id"" AS ""ArtistId"",
""AlbumId"",
SUM(COALESCE(""Size"", 0)) AS SizeOnDisk")
.Join<TrackFile, Album>((t, a) => t.AlbumId == a.Id)
.Join<Album, Artist>((album, artist) => album.ArtistMetadataId == artist.ArtistMetadataId)
.GroupBy<Artist>(x => x.Id)
.GroupBy<TrackFile>(x => x.AlbumId);
}
}
}