using System.Collections.Generic; using System.Linq; using Kavita.API.Repositories; using Kavita.Models.Entities; using Kavita.Models.Entities.Metadata; using Kavita.Models.Entities.Person; using Kavita.Models.Entities.User; using Microsoft.EntityFrameworkCore; namespace Kavita.Database.Extensions; public static class SearchQueryableExtensions { public static IQueryable Search(this IQueryable queryable, string searchQuery, int userId, AgeRestriction userRating) { return queryable .Where(uc => uc.Promoted || uc.AppUserId == userId) .Where(s => EF.Functions.Like(s.Title!, $"%{searchQuery}%") || EF.Functions.Like(s.NormalizedTitle!, $"%{searchQuery}%")) .RestrictAgainstAgeRestriction(userRating) .OrderBy(s => s.NormalizedTitle.Length) .ThenBy(s => s.NormalizedTitle); } public static IQueryable Search(this IQueryable queryable, string searchQuery, int userId, AgeRestriction userRating) { return queryable .Where(rl => rl.AppUserId == userId || rl.Promoted) .Where(rl => EF.Functions.Like(rl.Title, $"%{searchQuery}%")) .RestrictAgainstAgeRestriction(userRating) .OrderBy(s => s.NormalizedTitle.Length) .ThenBy(s => s.NormalizedTitle); } public static IQueryable Search(this IQueryable queryable, string searchQuery, int userId, IEnumerable libraryIds) { return queryable .Where(l => libraryIds.Contains(l.Id)) .Where(l => EF.Functions.Like(l.Name, $"%{searchQuery}%")) .IsRestricted(QueryContext.Search) .AsSplitQuery() .OrderBy(l => l.Name.ToLower()); } public static IQueryable SearchPeople(this IQueryable queryable, string searchQuery, IEnumerable seriesIds) { // Get people from SeriesMetadata var peopleFromSeriesMetadata = queryable .Where(sm => seriesIds.Contains(sm.SeriesId)) .SelectMany(sm => sm.People.Select(sp => sp.Person)) .Where(p => EF.Functions.Like(p.Name, $"%{searchQuery}%") || p.Aliases.Any(pa => EF.Functions.Like(pa.Alias, $"%{searchQuery}%")) ); var peopleFromChapterPeople = queryable .Where(sm => seriesIds.Contains(sm.SeriesId)) .SelectMany(sm => sm.Series.Volumes) .SelectMany(v => v.Chapters) .SelectMany(ch => ch.People.Select(cp => cp.Person)) .Where(p => EF.Functions.Like(p.Name, $"%{searchQuery}%") || p.Aliases.Any(pa => EF.Functions.Like(pa.Alias, $"%{searchQuery}%")) ); // Combine both queries and ensure distinct results return peopleFromSeriesMetadata .Union(peopleFromChapterPeople) .Select(p => p) .OrderBy(p => p.NormalizedName); } public static IQueryable SearchGenres(this IQueryable queryable, string searchQuery, IEnumerable seriesIds) { return queryable .Where(sm => seriesIds.Contains(sm.SeriesId)) .SelectMany(sm => sm.Genres.Where(t => EF.Functions.Like(t.Title, $"%{searchQuery}%"))) .Distinct() .OrderBy(t => t.NormalizedTitle.Length) .ThenBy(t => t.NormalizedTitle); } public static IQueryable SearchTags(this IQueryable queryable, string searchQuery, IEnumerable seriesIds) { return queryable .Where(sm => seriesIds.Contains(sm.SeriesId)) .SelectMany(sm => sm.Tags.Where(t => EF.Functions.Like(t.Title, $"%{searchQuery}%"))) .AsSplitQuery() .Distinct() .OrderBy(t => t.NormalizedTitle.Length) .ThenBy(t => t.NormalizedTitle); } }