Optimize series and presentation unique key grouping to preserve sort order and reduce memory usage

This commit is contained in:
2026-07-14 11:26:18 -04:00
parent 8914f4dce9
commit 59f0c20872
@@ -890,17 +890,28 @@ public sealed class BaseItemRepository
/// Applies series grouping at the database level using GROUP BY to avoid loading all episodes into memory.
/// This is crucial for TV Shows performance - instead of loading 10,000 episodes and deduplicating in-memory,
/// we group by SeriesPresentationUniqueKey at the database level and only load one episode per series.
/// IMPORTANT: Gets sorted IDs first to preserve sort order from the ORDER BY clause.
/// </summary>
private async Task<List<Guid>> ApplySeriesGroupingAtDatabaseLevel(IQueryable<BaseItemEntity> dbQuery, JellyfinDbContext context, CancellationToken cancellationToken)
{
// Get distinct series presentation keys along with one ID per series
// This uses GROUP BY at the database level instead of in-memory DISTINCT
var groupedBySeriesIds = await dbQuery
.GroupBy(e => e.TvExtras!.SeriesPresentationUniqueKey)
.Select(g => g.First().Id)
// Get all IDs in sorted order FIRST (preserves the ORDER BY from ApplyOrder)
var sortedIds = await dbQuery
.Select(e => new { e.Id, SeriesPresentationUniqueKey = e.TvExtras!.SeriesPresentationUniqueKey })
.ToListAsync(cancellationToken)
.ConfigureAwait(false);
// Deduplicate by SeriesPresentationUniqueKey while preserving the sorted order
var seenSeries = new HashSet<string>();
var groupedBySeriesIds = new List<Guid>();
foreach (var item in sortedIds)
{
if (seenSeries.Add(item.SeriesPresentationUniqueKey ?? string.Empty))
{
groupedBySeriesIds.Add(item.Id);
}
}
return groupedBySeriesIds;
}
@@ -908,17 +919,28 @@ public sealed class BaseItemRepository
/// Applies presentation unique key grouping at the database level using GROUP BY to avoid loading duplicate items into memory.
/// This optimization applies to movies, music videos, and other media types that can have duplicate PresentationUniqueKey values.
/// Instead of loading all items and deduplicating in-memory, we group by PresentationUniqueKey at the database level.
/// IMPORTANT: Gets sorted IDs first to preserve sort order from the ORDER BY clause.
/// </summary>
private async Task<List<Guid>> ApplyPresentationUniqueKeyGrouping(IQueryable<BaseItemEntity> dbQuery, JellyfinDbContext context, CancellationToken cancellationToken)
{
// Get distinct presentation keys along with one ID per unique key
// This uses GROUP BY at the database level instead of in-memory DISTINCT
var groupedByPresentationKeyIds = await dbQuery
.GroupBy(e => e.PresentationUniqueKey)
.Select(g => g.First().Id)
// Get all IDs in sorted order FIRST (preserves the ORDER BY from ApplyOrder)
var sortedIds = await dbQuery
.Select(e => new { e.Id, e.PresentationUniqueKey })
.ToListAsync(cancellationToken)
.ConfigureAwait(false);
// Deduplicate by PresentationUniqueKey while preserving the sorted order
var seenKeys = new HashSet<string>();
var groupedByPresentationKeyIds = new List<Guid>();
foreach (var item in sortedIds)
{
if (seenKeys.Add(item.PresentationUniqueKey ?? string.Empty))
{
groupedByPresentationKeyIds.Add(item.Id);
}
}
return groupedByPresentationKeyIds;
}