aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorexcelsior-dev <44299346+excelsior-dev@users.noreply.github.com>2026-10-05 19:18:49 -0400
committerCody Robibero <cody@robibe.ro>2026-10-05 19:18:49 -0400
commit3f087eb4a54c7088e381d9e466a2978b6fb30e30 (patch)
tree0f7620921df26119f428e81449655102d7642da5
parent6983363761b34d7efaef8d9074e874cae481cb1e (diff)
Backport pull request #18223 from jellyfin/release-12.z
Enhance internal search provider for multiple scripts Original-merge: 9ea203c47aa9f9723a22853151bf8291be3ed330 Merged-by: crobibero <cody@robibe.ro> Backported-by: Cody Robibero <cody@robibe.ro>
-rw-r--r--CONTRIBUTORS.md1
-rw-r--r--Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs31
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/SqlSearchProviderTests.cs10
3 files changed, 37 insertions, 5 deletions
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index aca9d6b790..8240b458e6 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -244,6 +244,7 @@
- [Oggeb1](https://github.com/Oggeb1)
- [scr4bble](https://github.com/scr4bble)
- [Mateus Bandeira](https://github.com/mateusbandeiraa)
+ - [excelsior](https://github.com/excelsior-dev)
- [TheDreadPirate](https://github.com/thedreaddpirate)
- [klizas](https://github.com/klizas)
diff --git a/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs b/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs
index 2cbfb6a4fa..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,9 +118,21 @@ 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);
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/SqlSearchProviderTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/SqlSearchProviderTests.cs
index 5aa770b9b3..89ca037256 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/SqlSearchProviderTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/SqlSearchProviderTests.cs
@@ -9,8 +9,11 @@ using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Server.Implementations.Item;
using Jellyfin.Server.Implementations.Tests.Item;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Configuration;
+using Microsoft.Extensions.Configuration;
using Moq;
using Xunit;
using BaseItemKind = Jellyfin.Data.Enums.BaseItemKind;
@@ -52,12 +55,17 @@ public sealed class SqlSearchProviderTests : SqliteDbTestFixture
var userManager = new Mock<IUserManager>();
userManager.Setup(u => u.GetUserById(_user.Id)).Returns(_user);
+ var serverConfigurationManager = new Mock<IServerConfigurationManager>();
+ serverConfigurationManager.Setup(scm => scm.Configuration)
+ .Returns(new ServerConfiguration());
+
_provider = new SqlSearchProvider(
CreateDbContextFactory(),
itemTypeLookup,
_libraryManager.Object,
userManager.Object,
- CreateBaseItemRepository(itemTypeLookup));
+ CreateBaseItemRepository(itemTypeLookup),
+ serverConfigurationManager.Object);
}
[Fact]