diff options
Diffstat (limited to 'src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs')
| -rw-r--r-- | src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs | 58 |
1 files changed, 44 insertions, 14 deletions
diff --git a/src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs b/src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs index 6b08f8dd7e..b821476390 100644 --- a/src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs +++ b/src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs @@ -32,18 +32,36 @@ public static class DescendantQueryHelper { ArgumentNullException.ThrowIfNull(context); - var (closureRoots, linkRoots) = ResolveLinkedRoots(context, parentId); + return AllDescendants(context, [parentId]) + .Where(e => !e.Equals(parentId)) + .Distinct(); + } - var hierarchyDescendants = ClosureDescendants(context, closureRoots); + /// <summary> + /// Gets all descendant IDs for multiple parent items in a single traversal. + /// Traverses AncestorIds and LinkedChildren, like <see cref="GetAllDescendantIds"/>, but resolves + /// the roots once for all seeds instead of once per seed. + /// </summary> + /// <param name="context">Database context.</param> + /// <param name="parentIds">Parent item IDs.</param> + /// <returns>Set of all descendant item IDs (excluding the parent IDs themselves).</returns> + public static HashSet<Guid> GetAllDescendantIdsBatch(JellyfinDbContext context, IReadOnlyList<Guid> parentIds) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(parentIds); - var linkedDescendants = context.LinkedChildren - .WhereOneOrMany(linkRoots, e => e.ParentId) - .Select(e => e.ChildId); + if (parentIds.Count == 0) + { + return []; + } - return hierarchyDescendants - .Concat(linkedDescendants) - .Where(e => !e.Equals(parentId)) - .Distinct(); + var descendants = AllDescendants(context, parentIds) + .Distinct() + .ToHashSet(); + + descendants.ExceptWith(parentIds); + + return descendants; } /// <summary> @@ -216,6 +234,18 @@ public static class DescendantQueryHelper return query; } + private static IQueryable<Guid> AllDescendants(JellyfinDbContext context, IReadOnlyList<Guid> parentIds) + { + var (closureRoots, linkRoots) = ResolveLinkedRoots(context, parentIds); + + var linkedDescendants = context.LinkedChildren + .WhereOneOrMany(linkRoots, e => e.ParentId) + .Select(e => e.ChildId); + + return ClosureDescendants(context, closureRoots) + .Concat(linkedDescendants); + } + private static IQueryable<Guid> ClosureDescendants(JellyfinDbContext context, IReadOnlyList<Guid> roots) { var direct = context.AncestorIds @@ -286,12 +316,12 @@ public static class DescendantQueryHelper // Resolves the roots the descendant sub-selects are anchored on: those contributing their closure, // and those contributing their linked children. - private static (List<Guid> ClosureRoots, List<Guid> LinkRoots) ResolveLinkedRoots(JellyfinDbContext context, Guid parentId) + private static (List<Guid> ClosureRoots, List<Guid> LinkRoots) ResolveLinkedRoots(JellyfinDbContext context, IReadOnlyList<Guid> parentIds) { - var closureRoots = new List<Guid> { parentId }; - var linkRoots = new List<Guid> { parentId }; - var visited = new HashSet<Guid> { parentId }; - var frontier = new List<Guid> { parentId }; + var visited = new HashSet<Guid>(parentIds); + var closureRoots = visited.ToList(); + var linkRoots = visited.ToList(); + var frontier = visited.ToList(); while (frontier.Count != 0) { |
