mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-03 23:36:38 +01:00
Add early tag check exit and enhance search ordering
- BaseItem: Skip GetInheritedTags() call for users without tag restrictions, improving visibility check performance - BaseItem: Only fetch parents once in visibility chec - OrderMapper: Include OriginalTitle in search relevance scoring for better matching of foreign content
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
#pragma warning disable RS0030 // Do not use banned APIs
|
||||
#pragma warning disable CA1304 // Specify CultureInfo
|
||||
#pragma warning disable CA1311 // Specify a culture or use an invariant version
|
||||
#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -57,10 +60,18 @@ public static class OrderMapper
|
||||
(ItemSortBy.SeriesDatePlayed, not null) => e =>
|
||||
jellyfinDbContext.BaseItems
|
||||
.Where(w => w.SeriesPresentationUniqueKey == e.PresentationUniqueKey)
|
||||
.Join(jellyfinDbContext.UserData.Where(w => w.UserId == query.User.Id && w.Played), f => f.Id, f => f.ItemId, (item, userData) => userData.LastPlayedDate)
|
||||
.LeftJoin(
|
||||
jellyfinDbContext.UserData.Where(w => w.UserId == query.User.Id && w.Played),
|
||||
item => item.Id,
|
||||
userData => userData.ItemId,
|
||||
(item, userData) => userData == null ? (DateTime?)null : userData.LastPlayedDate)
|
||||
.Max(f => f),
|
||||
(ItemSortBy.SeriesDatePlayed, null) => e => jellyfinDbContext.BaseItems.Where(w => w.SeriesPresentationUniqueKey == e.PresentationUniqueKey)
|
||||
.Join(jellyfinDbContext.UserData.Where(w => w.Played), f => f.Id, f => f.ItemId, (item, userData) => userData.LastPlayedDate)
|
||||
.LeftJoin(
|
||||
jellyfinDbContext.UserData.Where(w => w.Played),
|
||||
item => item.Id,
|
||||
userData => userData.ItemId,
|
||||
(item, userData) => userData == null ? (DateTime?)null : userData.LastPlayedDate)
|
||||
.Max(f => f),
|
||||
// ItemSortBy.SeriesDatePlayed => e => jellyfinDbContext.UserData
|
||||
// .Where(u => u.Item!.SeriesPresentationUniqueKey == e.PresentationUniqueKey && u.Played)
|
||||
@@ -73,6 +84,7 @@ public static class OrderMapper
|
||||
/// <summary>
|
||||
/// Creates an expression to order search results by match quality.
|
||||
/// Prioritizes: exact match (0) > prefix match with word boundary (1) > prefix match (2) > contains (3).
|
||||
/// Considers both CleanName and OriginalTitle for matching.
|
||||
/// </summary>
|
||||
/// <param name="searchTerm">The search term to match against.</param>
|
||||
/// <returns>An expression that returns an integer representing match quality (lower is better).</returns>
|
||||
@@ -80,10 +92,15 @@ public static class OrderMapper
|
||||
{
|
||||
var cleanSearchTerm = GetCleanValue(searchTerm);
|
||||
var searchPrefix = cleanSearchTerm + " ";
|
||||
var originalSearchLower = searchTerm.ToLowerInvariant();
|
||||
var originalSearchPrefix = originalSearchLower + " ";
|
||||
return e =>
|
||||
e.CleanName == cleanSearchTerm ? 0 :
|
||||
e.CleanName!.StartsWith(searchPrefix) ? 1 :
|
||||
e.CleanName!.StartsWith(cleanSearchTerm) ? 2 : 3;
|
||||
// Exact match on CleanName or OriginalTitle
|
||||
(e.CleanName == cleanSearchTerm || (e.OriginalTitle != null && e.OriginalTitle.ToLower() == originalSearchLower)) ? 0 :
|
||||
// Prefix match with word boundary
|
||||
(e.CleanName!.StartsWith(searchPrefix) || (e.OriginalTitle != null && e.OriginalTitle.ToLower().StartsWith(originalSearchPrefix))) ? 1 :
|
||||
// Prefix match
|
||||
(e.CleanName!.StartsWith(cleanSearchTerm) || (e.OriginalTitle != null && e.OriginalTitle.ToLower().StartsWith(originalSearchLower))) ? 2 : 3;
|
||||
}
|
||||
|
||||
private static string GetCleanValue(string value)
|
||||
|
||||
@@ -1334,6 +1334,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
var parents = GetParents().ToList();
|
||||
if (GetParents().Any(i => !i.IsVisible(user, true)))
|
||||
{
|
||||
return false;
|
||||
@@ -1341,7 +1342,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
if (checkFolders)
|
||||
{
|
||||
var topParent = GetParents().LastOrDefault() ?? this;
|
||||
var topParent = parents.Count > 0 ? parents[^1] : this;
|
||||
|
||||
if (string.IsNullOrEmpty(topParent.Path))
|
||||
{
|
||||
@@ -1670,8 +1671,16 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
private bool IsVisibleViaTags(User user, bool skipAllowedTagsCheck)
|
||||
{
|
||||
var allowedTagsPreference = user.GetPreference(PreferenceKind.AllowedTags);
|
||||
var blockedTagsPreference = user.GetPreference(PreferenceKind.BlockedTags);
|
||||
var needsTagCheck = allowedTagsPreference.Length > 0 || blockedTagsPreference.Length > 0;
|
||||
if (!needsTagCheck)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var allTags = GetInheritedTags();
|
||||
if (user.GetPreference(PreferenceKind.BlockedTags).Any(i => allTags.Contains(i, StringComparison.OrdinalIgnoreCase)))
|
||||
if (blockedTagsPreference.Any(i => allTags.Contains(i, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1682,8 +1691,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
return true;
|
||||
}
|
||||
|
||||
var allowedTagsPreference = user.GetPreference(PreferenceKind.AllowedTags);
|
||||
if (!skipAllowedTagsCheck && allowedTagsPreference.Length != 0 && !allowedTagsPreference.Any(i => allTags.Contains(i, StringComparison.OrdinalIgnoreCase)))
|
||||
if (!skipAllowedTagsCheck && !allowedTagsPreference.Any(i => allTags.Contains(i, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user