aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs')
-rw-r--r--Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs38
1 files changed, 34 insertions, 4 deletions
diff --git a/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs b/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs
index c4d3b249d5..a5ea02df51 100644
--- a/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs
+++ b/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs
@@ -10,6 +10,7 @@ using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Extensions;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
@@ -36,6 +37,7 @@ public class SqlSearchProvider : IInternalSearchProvider
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IItemQueryHelpers _queryHelpers;
+ private readonly IServerConfigurationManager _configurationManager;
/// <summary>
/// Initializes a new instance of the <see cref="SqlSearchProvider"/> class.
@@ -45,18 +47,21 @@ public class SqlSearchProvider : IInternalSearchProvider
/// <param name="libraryManager">The library manager.</param>
/// <param name="userManager">The user manager.</param>
/// <param name="queryHelpers">The shared item query helpers.</param>
+ /// <param name="configurationManager">The configuration manager.</param>
public SqlSearchProvider(
IDbContextFactory<JellyfinDbContext> dbProvider,
IItemTypeLookup itemTypeLookup,
ILibraryManager libraryManager,
IUserManager userManager,
- IItemQueryHelpers queryHelpers)
+ IItemQueryHelpers queryHelpers,
+ IServerConfigurationManager configurationManager)
{
_dbProvider = dbProvider;
_itemTypeLookup = itemTypeLookup;
_libraryManager = libraryManager;
_userManager = userManager;
_queryHelpers = queryHelpers;
+ _configurationManager = configurationManager;
}
/// <inheritdoc/>
@@ -98,6 +103,12 @@ public class SqlSearchProvider : IInternalSearchProvider
// so match it via a case-insensitive LIKE rather than a per-row case conversion
// that may not translate to SQL on every provider.
var likeOriginal = $"%{rawSearchTerm}%";
+
+ // Great benefit for users with metadata in non-latin script
+ // since SortName is among other things stored transliterated.
+ var sortNameShape = BaseItem.GetSortName(rawSearchTerm, true, _configurationManager.Configuration);
+ var likeSortName = string.IsNullOrWhiteSpace(sortNameShape) ? null : $"%{sortNameShape}%";
+
var limit = query.Limit ?? DefaultSearchLimit;
var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
@@ -107,14 +118,27 @@ public class SqlSearchProvider : IInternalSearchProvider
var dbQuery = dbContext.BaseItems
.AsNoTracking()
.Where(e => e.Id != _placeholderId)
- .Where(e => !e.IsVirtualItem)
- .Where(e => e.CleanName!.Contains(cleanSearchTerm)
- || (e.OriginalTitle != null && EF.Functions.Like(e.OriginalTitle, likeOriginal)));
+ .Where(e => !e.IsVirtualItem);
+
+ if (likeSortName is null)
+ {
+ dbQuery = dbQuery
+ .Where(e => e.CleanName!.Contains(cleanSearchTerm)
+ || (e.OriginalTitle != null && EF.Functions.Like(e.OriginalTitle, likeOriginal)));
+ }
+ else
+ {
+ dbQuery = dbQuery
+ .Where(e => e.CleanName!.Contains(cleanSearchTerm)
+ || (e.OriginalTitle != null && EF.Functions.Like(e.OriginalTitle, likeOriginal))
+ || (e.SortName != null && EF.Functions.Like(e.SortName, likeSortName)));
+ }
dbQuery = ApplyTypeFilter(dbQuery, query.IncludeItemTypes, query.ExcludeItemTypes);
dbQuery = ApplyMediaTypeFilter(dbQuery, query.MediaTypes);
dbQuery = ApplyParentFilter(dbQuery, query.ParentId);
dbQuery = ApplyUserAccessFilter(dbContext, dbQuery, query);
+ dbQuery = ExcludeVersionsOfMatchedPrimaries(dbQuery);
// Compute the score in SQL: the ternary translates to a CASE WHEN. CleanName is
// the pre-normalized (lowercase, diacritic-stripped) form, so we score against it
@@ -193,6 +217,12 @@ public class SqlSearchProvider : IInternalSearchProvider
return query.Where(e => e.ParentId == pid || e.Parents!.Any(p => p.ParentItemId == pid));
}
+ private static IQueryable<BaseItemEntity> ExcludeVersionsOfMatchedPrimaries(IQueryable<BaseItemEntity> query)
+ {
+ var matched = query;
+ return query.Where(e => e.PrimaryVersionId == null || !matched.Any(p => p.Id == e.PrimaryVersionId));
+ }
+
private IQueryable<BaseItemEntity> ApplyUserAccessFilter(
JellyfinDbContext dbContext,
IQueryable<BaseItemEntity> query,