Optimize series and presentation unique key grouping to preserve sort order and reduce memory usage
This commit is contained in:
@@ -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.
|
/// 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,
|
/// 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.
|
/// 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>
|
/// </summary>
|
||||||
private async Task<List<Guid>> ApplySeriesGroupingAtDatabaseLevel(IQueryable<BaseItemEntity> dbQuery, JellyfinDbContext context, CancellationToken cancellationToken)
|
private async Task<List<Guid>> ApplySeriesGroupingAtDatabaseLevel(IQueryable<BaseItemEntity> dbQuery, JellyfinDbContext context, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Get distinct series presentation keys along with one ID per series
|
// Get all IDs in sorted order FIRST (preserves the ORDER BY from ApplyOrder)
|
||||||
// This uses GROUP BY at the database level instead of in-memory DISTINCT
|
var sortedIds = await dbQuery
|
||||||
var groupedBySeriesIds = await dbQuery
|
.Select(e => new { e.Id, SeriesPresentationUniqueKey = e.TvExtras!.SeriesPresentationUniqueKey })
|
||||||
.GroupBy(e => e.TvExtras!.SeriesPresentationUniqueKey)
|
|
||||||
.Select(g => g.First().Id)
|
|
||||||
.ToListAsync(cancellationToken)
|
.ToListAsync(cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.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;
|
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.
|
/// 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.
|
/// 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.
|
/// 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>
|
/// </summary>
|
||||||
private async Task<List<Guid>> ApplyPresentationUniqueKeyGrouping(IQueryable<BaseItemEntity> dbQuery, JellyfinDbContext context, CancellationToken cancellationToken)
|
private async Task<List<Guid>> ApplyPresentationUniqueKeyGrouping(IQueryable<BaseItemEntity> dbQuery, JellyfinDbContext context, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Get distinct presentation keys along with one ID per unique key
|
// Get all IDs in sorted order FIRST (preserves the ORDER BY from ApplyOrder)
|
||||||
// This uses GROUP BY at the database level instead of in-memory DISTINCT
|
var sortedIds = await dbQuery
|
||||||
var groupedByPresentationKeyIds = await dbQuery
|
.Select(e => new { e.Id, e.PresentationUniqueKey })
|
||||||
.GroupBy(e => e.PresentationUniqueKey)
|
|
||||||
.Select(g => g.First().Id)
|
|
||||||
.ToListAsync(cancellationToken)
|
.ToListAsync(cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.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;
|
return groupedByPresentationKeyIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user