diff options
| author | Shadowghost <Shadowghost@users.noreply.github.com> | 2026-10-05 19:18:43 -0400 |
|---|---|---|
| committer | Cody Robibero <cody@robibe.ro> | 2026-10-05 19:18:43 -0400 |
| commit | 3d52637ce320aaa592008f4a05cfc3613e0d36fe (patch) | |
| tree | 1021119e0fc182ae51a91529b27ea1f3801db18a /Jellyfin.Server.Implementations | |
| parent | 6c753e8d71c21d20c7967a5583f139171c94c845 (diff) | |
Backport pull request #18093 from jellyfin/release-12.z
Stop rebuilding the inherited-tag set per row, and count only the people /Persons can return
Original-merge: 63b5a050a9dc943854bae01856f30f1a5a089245
Merged-by: crobibero <cody@robibe.ro>
Backported-by: Cody Robibero <cody@robibe.ro>
Diffstat (limited to 'Jellyfin.Server.Implementations')
3 files changed, 27 insertions, 4 deletions
diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs index 1a4c9da41c..4cd02bcd8d 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.QueryBuilding.cs @@ -612,11 +612,12 @@ public sealed partial class BaseItemRepository var blockedTagItemIds = context.ItemValuesMap .Where(f => f.ItemValue.Type == ItemValueType.Tags && excludedTags.Contains(f.ItemValue.CleanValue)) .Select(f => f.ItemId); + var blockedByAncestor = ItemsBelowTaggedAncestor(context, blockedTagItemIds); baseQuery = baseQuery.Where(e => !blockedTagItemIds.Contains(e.Id) && !(e.SeriesId.HasValue && blockedTagItemIds.Contains(e.SeriesId.Value)) - && !e.Parents!.Any(p => blockedTagItemIds.Contains(p.ParentItemId)) + && !blockedByAncestor.Contains(e.Id) && !(e.TopParentId.HasValue && blockedTagItemIds.Contains(e.TopParentId.Value))); } @@ -629,10 +630,12 @@ public sealed partial class BaseItemRepository .Where(f => f.ItemValue.Type == ItemValueType.Tags && includeTags.Contains(f.ItemValue.CleanValue)) .Select(f => f.ItemId); + var allowedByAncestor = ItemsBelowTaggedAncestor(context, allowedTagItemIds); + baseQuery = baseQuery.Where(e => allowedTagItemIds.Contains(e.Id) || (e.SeriesId.HasValue && allowedTagItemIds.Contains(e.SeriesId.Value)) - || e.Parents!.Any(p => allowedTagItemIds.Contains(p.ParentItemId)) + || allowedByAncestor.Contains(e.Id) || (e.TopParentId.HasValue && allowedTagItemIds.Contains(e.TopParentId.Value)) // People don't carry the tags of the media they appear in and would never match @@ -643,6 +646,17 @@ public sealed partial class BaseItemRepository } /// <summary> + /// Reads back the items that carry one of the tagged items as an ancestor. + /// </summary> + /// <param name="context">The database context.</param> + /// <param name="taggedItemIds">The ids of the items carrying the tag.</param> + /// <returns>The ids of the items below one of them.</returns> + private static IQueryable<Guid> ItemsBelowTaggedAncestor(JellyfinDbContext context, IQueryable<Guid> taggedItemIds) + => context.AncestorIds + .Where(a => taggedItemIds.Contains(a.ParentItemId)) + .Select(a => a.ItemId); + + /// <summary> /// Builds a filter expression for max parental rating that handles both rated items /// and unrated BoxSets/Playlists (which check linked children's ratings). /// </summary> diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs index a7110485b4..e66f18bc6e 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs @@ -1132,11 +1132,12 @@ public sealed partial class BaseItemRepository var blockedTagItemIds = context.ItemValuesMap .Where(f => f.ItemValue.Type == ItemValueType.Tags && excludedTags.Contains(f.ItemValue.CleanValue)) .Select(f => f.ItemId); + var blockedByAncestor = ItemsBelowTaggedAncestor(context, blockedTagItemIds); baseQuery = baseQuery.Where(e => !blockedTagItemIds.Contains(e.Id) && !(e.SeriesId.HasValue && blockedTagItemIds.Contains(e.SeriesId.Value)) - && !e.Parents!.Any(p => blockedTagItemIds.Contains(p.ParentItemId)) + && !blockedByAncestor.Contains(e.Id) && !(e.TopParentId.HasValue && blockedTagItemIds.Contains(e.TopParentId.Value))); } @@ -1148,11 +1149,12 @@ public sealed partial class BaseItemRepository var allowedTagItemIds = context.ItemValuesMap .Where(f => f.ItemValue.Type == ItemValueType.Tags && includeTags.Contains(f.ItemValue.CleanValue)) .Select(f => f.ItemId); + var allowedByAncestor = ItemsBelowTaggedAncestor(context, allowedTagItemIds); baseQuery = baseQuery.Where(e => allowedTagItemIds.Contains(e.Id) || (e.SeriesId.HasValue && allowedTagItemIds.Contains(e.SeriesId.Value)) - || e.Parents!.Any(p => allowedTagItemIds.Contains(p.ParentItemId)) + || allowedByAncestor.Contains(e.Id) || (e.TopParentId.HasValue && allowedTagItemIds.Contains(e.TopParentId.Value)) // People don't carry the tags of the media they appear in and would never match diff --git a/Jellyfin.Server.Implementations/Item/PeopleRepository.cs b/Jellyfin.Server.Implementations/Item/PeopleRepository.cs index fcddc09ad9..bc79699486 100644 --- a/Jellyfin.Server.Implementations/Item/PeopleRepository.cs +++ b/Jellyfin.Server.Implementations/Item/PeopleRepository.cs @@ -423,6 +423,13 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider, I .Any(m => m.PeopleId == e.Id && accessibleItems.Any(i => i.Id == m.ItemId))); } + if (filter.MustHaveItem) + { + // A credit with no by-name item behind it cannot be handed back as one. + var personType = itemTypeLookup.BaseItemKindNames[BaseItemKind.Person]; + query = query.Where(e => context.BaseItems.Any(b => b.Type == personType && b.Name == e.Name)); + } + if (!filter.ItemId.IsEmpty()) { var itemId = filter.ItemId; |
