| | | 1 | | #pragma warning disable RS0030 // Do not use banned APIs |
| | | 2 | | |
| | | 3 | | using System; |
| | | 4 | | using System.Linq; |
| | | 5 | | using System.Linq.Expressions; |
| | | 6 | | using Jellyfin.Database.Implementations; |
| | | 7 | | using Jellyfin.Database.Implementations.Entities; |
| | | 8 | | |
| | | 9 | | namespace Jellyfin.Server.Implementations.Item; |
| | | 10 | | |
| | | 11 | | /// <summary> |
| | | 12 | | /// Extension methods for applying folder-aware filters that check items and their descendants. |
| | | 13 | | /// </summary> |
| | | 14 | | internal static class FolderAwareFilterExtensions |
| | | 15 | | { |
| | | 16 | | /// <summary> |
| | | 17 | | /// Filters items where either the item matches the condition (for non-folders) |
| | | 18 | | /// or any descendant matches (for folders). Uses reverse traversal through AncestorIds. |
| | | 19 | | /// </summary> |
| | | 20 | | /// <param name="query">The query to filter.</param> |
| | | 21 | | /// <param name="context">The database context.</param> |
| | | 22 | | /// <param name="condition">The condition to check on BaseItemEntity.</param> |
| | | 23 | | /// <returns>Filtered query.</returns> |
| | | 24 | | public static IQueryable<BaseItemEntity> WhereItemOrDescendantMatches( |
| | | 25 | | this IQueryable<BaseItemEntity> query, |
| | | 26 | | JellyfinDbContext context, |
| | | 27 | | Expression<Func<BaseItemEntity, bool>> condition) |
| | | 28 | | { |
| | | 29 | | // Get IDs of items that directly match the condition |
| | 0 | 30 | | var directMatchIds = context.BaseItems.Where(condition).Select(b => b.Id); |
| | | 31 | | |
| | | 32 | | // Get parent IDs where a descendant (via AncestorIds) matches |
| | 0 | 33 | | var ancestorMatchIds = context.AncestorIds |
| | 0 | 34 | | .Where(a => directMatchIds.Contains(a.ItemId)) |
| | 0 | 35 | | .Select(a => a.ParentItemId); |
| | | 36 | | |
| | | 37 | | // Get parent IDs where a linked child matches |
| | 0 | 38 | | var linkedMatchIds = context.LinkedChildren |
| | 0 | 39 | | .Where(lc => directMatchIds.Contains(lc.ChildId)) |
| | 0 | 40 | | .Select(lc => lc.ParentId); |
| | | 41 | | |
| | 0 | 42 | | var allMatchingIds = directMatchIds |
| | 0 | 43 | | .Concat(ancestorMatchIds) |
| | 0 | 44 | | .Concat(linkedMatchIds) |
| | 0 | 45 | | .Distinct(); |
| | | 46 | | |
| | 0 | 47 | | return query.Where(e => allMatchingIds.Contains(e.Id)); |
| | | 48 | | } |
| | | 49 | | |
| | | 50 | | /// <summary> |
| | | 51 | | /// Filters items where neither the item matches the condition (for non-folders) |
| | | 52 | | /// nor any descendant matches (for folders). Uses reverse traversal for infinite depth. |
| | | 53 | | /// </summary> |
| | | 54 | | /// <param name="query">The query to filter.</param> |
| | | 55 | | /// <param name="context">The database context.</param> |
| | | 56 | | /// <param name="condition">The condition that should NOT match.</param> |
| | | 57 | | /// <returns>Filtered query.</returns> |
| | | 58 | | public static IQueryable<BaseItemEntity> WhereNeitherItemNorDescendantMatches( |
| | | 59 | | this IQueryable<BaseItemEntity> query, |
| | | 60 | | JellyfinDbContext context, |
| | | 61 | | Expression<Func<BaseItemEntity, bool>> condition) |
| | | 62 | | { |
| | | 63 | | // Get IDs of items that directly match the condition |
| | 0 | 64 | | var directMatchIds = context.BaseItems.Where(condition).Select(b => b.Id); |
| | | 65 | | |
| | | 66 | | // Get parent IDs where a descendant (via AncestorIds) matches |
| | 0 | 67 | | var ancestorMatchIds = context.AncestorIds |
| | 0 | 68 | | .Where(a => directMatchIds.Contains(a.ItemId)) |
| | 0 | 69 | | .Select(a => a.ParentItemId); |
| | | 70 | | |
| | | 71 | | // Get parent IDs where a linked child matches |
| | 0 | 72 | | var linkedMatchIds = context.LinkedChildren |
| | 0 | 73 | | .Where(lc => directMatchIds.Contains(lc.ChildId)) |
| | 0 | 74 | | .Select(lc => lc.ParentId); |
| | | 75 | | |
| | 0 | 76 | | var allMatchingIds = directMatchIds |
| | 0 | 77 | | .Concat(ancestorMatchIds) |
| | 0 | 78 | | .Concat(linkedMatchIds) |
| | 0 | 79 | | .Distinct(); |
| | | 80 | | |
| | 0 | 81 | | return query.Where(e => !allMatchingIds.Contains(e.Id)); |
| | | 82 | | } |
| | | 83 | | } |