aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2026-07-29 09:35:20 +0200
committerShadowghost <Ghost_of_Stone@web.de>2026-07-29 09:35:20 +0200
commitd64e18b69a0a6089aab350f33464f362f09de942 (patch)
tree3b98a8feadfee28a108d9de0cbd6df726d2b2992 /Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs
parenteed664b7d351ae9a22b0722b18d65dcff3fc185d (diff)
Fix more filter cases
Diffstat (limited to 'Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs')
-rw-r--r--Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs67
1 files changed, 18 insertions, 49 deletions
diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs
index f5e2e9447a..80e16ca310 100644
--- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs
+++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs
@@ -503,62 +503,31 @@ public sealed partial class BaseItemRepository
}
/// <inheritdoc />
- public IQueryable<Guid> GetFoldersWithUnplayedItemsQuery(JellyfinDbContext context, IQueryable<Guid> folderIds, User user)
+ public IQueryable<BaseItemEntity> GetAccessFilteredLeafItemsQuery(JellyfinDbContext context, User user, bool includeOwnedItems = false)
{
ArgumentNullException.ThrowIfNull(context);
- ArgumentNullException.ThrowIfNull(folderIds);
ArgumentNullException.ThrowIfNull(user);
- var filter = new InternalItemsQuery(user);
- var userId = user.Id;
-
var leafItems = context.BaseItems
.AsNoTracking()
- .Where(b => !b.IsFolder && !b.IsVirtualItem);
- leafItems = ApplyAccessFiltering(context, leafItems, filter);
-
- // Only unplayed leaves are joined, so each branch is a semi-join per folder instead of a
- // played-vs-total count per folder. Folders with no leaves at all simply never match.
- var unplayedLeafItems = leafItems
- .Where(b => !b.UserData!.Any(ud => ud.UserId == userId && ud.Played))
- .Select(b => new { b.Id });
-
- var ancestorLeaves = context.AncestorIds
- .Where(a => folderIds.Contains(a.ParentItemId))
- .Join(
- unplayedLeafItems,
- a => a.ItemId,
- b => b.Id,
- (a, b) => a.ParentItemId);
+ .Where(e => !e.IsFolder && !e.IsVirtualItem);
- var linkedLeaves = context.LinkedChildren
- .Where(lc => folderIds.Contains(lc.ParentId))
- .Join(
- unplayedLeafItems,
- lc => lc.ChildId,
- b => b.Id,
- (lc, b) => lc.ParentId);
+ return ApplyAccessFiltering(context, leafItems, new InternalItemsQuery(user) { IncludeOwnedItems = includeOwnedItems });
+ }
- var linkedFolderLeaves = context.LinkedChildren
- .Where(lc => folderIds.Contains(lc.ParentId))
- .Join(
- context.BaseItems.Where(b => b.IsFolder),
- lc => lc.ChildId,
- b => b.Id,
- (lc, b) => new { lc.ParentId, FolderChildId = b.Id })
- .Join(
- context.AncestorIds,
- x => x.FolderChildId,
- a => a.ParentItemId,
- (x, a) => new { x.ParentId, DescendantId = a.ItemId })
- .Join(
- unplayedLeafItems,
- x => x.DescendantId,
- b => b.Id,
- (x, b) => x.ParentId);
-
- return ancestorLeaves
- .Union(linkedLeaves)
- .Union(linkedFolderLeaves);
+ /// <inheritdoc />
+ public Expression<Func<BaseItemEntity, bool>> BuildHasDescendantFilter(JellyfinDbContext context, IQueryable<BaseItemEntity> descendants)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(descendants);
+
+ // Descendants are reachable through the ancestor chain and - for BoxSets and Playlists - as
+ // linked children, which can themselves be folders contributing their own descendants.
+ // Every step is a correlated index seek, so only the rows the outer query keeps are visited
+ // and a folder is left as soon as its first matching descendant is found.
+ return e => context.AncestorIds.Any(a => a.ParentItemId == e.Id && descendants.Any(d => d.Id == a.ItemId))
+ || context.LinkedChildren.Any(lc => lc.ParentId == e.Id
+ && (descendants.Any(d => d.Id == lc.ChildId)
+ || context.AncestorIds.Any(a => a.ParentItemId == lc.ChildId && descendants.Any(d => d.Id == a.ItemId))));
}
}