using System; using System.Linq; using System.Linq.Expressions; using Jellyfin.Database.Implementations; using Jellyfin.Database.Implementations.Entities; namespace Jellyfin.Server.Implementations.Item; /// /// Extension methods for applying folder-aware filters that check items and their descendants. /// internal static class FolderAwareFilterExtensions { /// /// Filters items where either the item matches the condition (for non-folders) /// or any descendant matches (for folders). Uses reverse traversal through AncestorIds. /// /// The query to filter. /// The database context. /// The condition to check on BaseItemEntity. /// Filtered query. public static IQueryable WhereItemOrDescendantMatches( this IQueryable query, JellyfinDbContext context, Expression> condition) { var matchingIds = context.BaseItems.Where(condition).Select(b => b.Id); var foldersWithMatchingDescendants = context.AncestorIds .Where(a => matchingIds.Contains(a.ItemId)) .Select(a => a.ParentItemId) .Union(context.LinkedChildren .Where(lc => matchingIds.Contains(lc.ChildId)) .Select(lc => lc.ParentId)); return query.Where(e => matchingIds.Contains(e.Id) || foldersWithMatchingDescendants.Contains(e.Id)); } /// /// Filters items where neither the item matches the condition (for non-folders) /// nor any descendant matches (for folders). Uses reverse traversal for infinite depth. /// /// The query to filter. /// The database context. /// The condition that should NOT match. /// Filtered query. public static IQueryable WhereNeitherItemNorDescendantMatches( this IQueryable query, JellyfinDbContext context, Expression> condition) { var matchingIds = context.BaseItems.Where(condition).Select(b => b.Id); var foldersWithMatchingDescendants = context.AncestorIds .Where(a => matchingIds.Contains(a.ItemId)) .Select(a => a.ParentItemId) .Union(context.LinkedChildren .Where(lc => matchingIds.Contains(lc.ChildId)) .Select(lc => lc.ParentId)); return query.Where(e => !matchingIds.Contains(e.Id) && !foldersWithMatchingDescendants.Contains(e.Id)); } }