aboutsummaryrefslogtreecommitdiff
path: root/src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs')
-rw-r--r--src/Jellyfin.Database/Jellyfin.Database.Implementations/DescendantQueryHelper.cs58
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)
{