Cleaned up Search API to correct some SQL queries and speed it up slightly.
This commit is contained in:
parent
f907486c74
commit
99424acb48
2 changed files with 41 additions and 32 deletions
|
|
@ -47,20 +47,27 @@ public class SearchController : BaseApiController
|
||||||
return Ok(await _unitOfWork.SeriesRepository.GetSeriesForChapter(chapterId, userId));
|
return Ok(await _unitOfWork.SeriesRepository.GetSeriesForChapter(chapterId, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search for different entities against the query string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queryString"></param>
|
||||||
|
/// <returns></returns>
|
||||||
[HttpGet("search")]
|
[HttpGet("search")]
|
||||||
public async Task<ActionResult<SearchResultGroupDto>> Search(string queryString)
|
public async Task<ActionResult<SearchResultGroupDto>> Search(string queryString)
|
||||||
{
|
{
|
||||||
queryString = Uri.UnescapeDataString(queryString).Trim().Replace(@"%", string.Empty).Replace(":", string.Empty);
|
queryString = Uri.UnescapeDataString(queryString)
|
||||||
|
.Trim()
|
||||||
|
.Replace(@"%", string.Empty)
|
||||||
|
.Replace(":", string.Empty);
|
||||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||||
|
|
||||||
// Get libraries user has access to
|
// Get libraries user has access to
|
||||||
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
|
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(user.Id)).ToList();
|
||||||
|
if (!libraries.Any()) return BadRequest("User does not have access to any libraries");
|
||||||
|
|
||||||
if (!libraries.Any()) return BadRequest("User does not have access to any libraries");
|
|
||||||
if (!libraries.Any()) return BadRequest("User does not have access to any libraries");
|
|
||||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||||
|
var series = await _unitOfWork.SeriesRepository.SearchSeries(user.Id, isAdmin,
|
||||||
var series = await _unitOfWork.SeriesRepository.SearchSeries(user.Id, isAdmin, libraries.Select(l => l.Id).ToArray(), queryString);
|
libraries.Select(l => l.Id).ToArray(), queryString);
|
||||||
|
|
||||||
return Ok(series);
|
return Ok(series);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ public interface ISeriesRepository
|
||||||
/// <param name="libraryIds"></param>
|
/// <param name="libraryIds"></param>
|
||||||
/// <param name="searchQuery"></param>
|
/// <param name="searchQuery"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<SearchResultGroupDto> SearchSeries(int userId, bool isAdmin, int[] libraryIds, string searchQuery);
|
Task<SearchResultGroupDto> SearchSeries(int userId, bool isAdmin, int[] libraryIds, string searchQuery, int maxRecords = 15);
|
||||||
Task<IEnumerable<Series>> GetSeriesForLibraryIdAsync(int libraryId, SeriesIncludes includes = SeriesIncludes.None);
|
Task<IEnumerable<Series>> GetSeriesForLibraryIdAsync(int libraryId, SeriesIncludes includes = SeriesIncludes.None);
|
||||||
Task<SeriesDto> GetSeriesDtoByIdAsync(int seriesId, int userId);
|
Task<SeriesDto> GetSeriesDtoByIdAsync(int seriesId, int userId);
|
||||||
Task<Series> GetSeriesByIdAsync(int seriesId, SeriesIncludes includes = SeriesIncludes.Volumes | SeriesIncludes.Metadata);
|
Task<Series> GetSeriesByIdAsync(int seriesId, SeriesIncludes includes = SeriesIncludes.Volumes | SeriesIncludes.Metadata);
|
||||||
|
|
@ -287,27 +287,26 @@ public class SeriesRepository : ISeriesRepository
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SearchResultGroupDto> SearchSeries(int userId, bool isAdmin, int[] libraryIds, string searchQuery)
|
public async Task<SearchResultGroupDto> SearchSeries(int userId, bool isAdmin, int[] libraryIds, string searchQuery, int maxRecords = 15)
|
||||||
{
|
{
|
||||||
const int maxRecords = 15;
|
|
||||||
var result = new SearchResultGroupDto();
|
var result = new SearchResultGroupDto();
|
||||||
var searchQueryNormalized = Services.Tasks.Scanner.Parser.Parser.Normalize(searchQuery);
|
var searchQueryNormalized = Services.Tasks.Scanner.Parser.Parser.Normalize(searchQuery);
|
||||||
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||||
|
|
||||||
var seriesIds = _context.Series
|
var seriesIds = await _context.Series
|
||||||
.Where(s => libraryIds.Contains(s.LibraryId))
|
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||||
.RestrictAgainstAgeRestriction(userRating)
|
.RestrictAgainstAgeRestriction(userRating)
|
||||||
.Select(s => s.Id)
|
.Select(s => s.Id)
|
||||||
.ToList();
|
.ToListAsync();
|
||||||
|
|
||||||
result.Libraries = await _context.Library
|
result.Libraries = _context.Library
|
||||||
.Where(l => libraryIds.Contains(l.Id))
|
.Where(l => libraryIds.Contains(l.Id))
|
||||||
.Where(l => EF.Functions.Like(l.Name, $"%{searchQuery}%"))
|
.Where(l => EF.Functions.Like(l.Name, $"%{searchQuery}%"))
|
||||||
.OrderBy(l => l.Name)
|
.OrderBy(l => l.Name)
|
||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
.Take(maxRecords)
|
.Take(maxRecords)
|
||||||
.ProjectTo<LibraryDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<LibraryDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.AsEnumerable();
|
||||||
|
|
||||||
var justYear = Regex.Match(searchQuery, @"\d{4}").Value;
|
var justYear = Regex.Match(searchQuery, @"\d{4}").Value;
|
||||||
var hasYearInQuery = !string.IsNullOrEmpty(justYear);
|
var hasYearInQuery = !string.IsNullOrEmpty(justYear);
|
||||||
|
|
@ -329,56 +328,57 @@ public class SeriesRepository : ISeriesRepository
|
||||||
.ProjectTo<SearchResultDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<SearchResultDto>(_mapper.ConfigurationProvider)
|
||||||
.AsEnumerable();
|
.AsEnumerable();
|
||||||
|
|
||||||
result.ReadingLists = await _context.ReadingList
|
result.ReadingLists = _context.ReadingList
|
||||||
.Where(rl => rl.AppUserId == userId || rl.Promoted)
|
.Where(rl => rl.AppUserId == userId || rl.Promoted)
|
||||||
.Where(rl => EF.Functions.Like(rl.Title, $"%{searchQuery}%"))
|
.Where(rl => EF.Functions.Like(rl.Title, $"%{searchQuery}%"))
|
||||||
.RestrictAgainstAgeRestriction(userRating)
|
.RestrictAgainstAgeRestriction(userRating)
|
||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
|
.OrderBy(c => c.NormalizedTitle)
|
||||||
.Take(maxRecords)
|
.Take(maxRecords)
|
||||||
.ProjectTo<ReadingListDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<ReadingListDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.AsEnumerable();
|
||||||
|
|
||||||
result.Collections = await _context.CollectionTag
|
result.Collections = _context.CollectionTag
|
||||||
.Where(c => EF.Functions.Like(c.Title, $"%{searchQuery}%")
|
.Where(c => EF.Functions.Like(c.Title, $"%{searchQuery}%")
|
||||||
|| EF.Functions.Like(c.NormalizedTitle, $"%{searchQueryNormalized}%"))
|
|| EF.Functions.Like(c.NormalizedTitle, $"%{searchQueryNormalized}%"))
|
||||||
.Where(c => c.Promoted || isAdmin)
|
.Where(c => c.Promoted || isAdmin)
|
||||||
.RestrictAgainstAgeRestriction(userRating)
|
.RestrictAgainstAgeRestriction(userRating)
|
||||||
.OrderBy(s => s.Title)
|
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
|
.OrderBy(s => s.Title)
|
||||||
.Take(maxRecords)
|
.Take(maxRecords)
|
||||||
.OrderBy(c => c.NormalizedTitle)
|
|
||||||
.ProjectTo<CollectionTagDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<CollectionTagDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.AsEnumerable();
|
||||||
|
|
||||||
result.Persons = await _context.SeriesMetadata
|
result.Persons = _context.SeriesMetadata
|
||||||
.Where(sm => seriesIds.Contains(sm.SeriesId))
|
.Where(sm => seriesIds.Contains(sm.SeriesId))
|
||||||
.SelectMany(sm => sm.People.Where(t => EF.Functions.Like(t.Name, $"%{searchQuery}%")))
|
.SelectMany(sm => sm.People.Where(t => EF.Functions.Like(t.Name, $"%{searchQuery}%")))
|
||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
.Take(maxRecords)
|
|
||||||
.Distinct()
|
.Distinct()
|
||||||
|
.OrderBy(p => p.NormalizedName)
|
||||||
|
.Take(maxRecords)
|
||||||
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.AsEnumerable();
|
||||||
|
|
||||||
result.Genres = await _context.SeriesMetadata
|
result.Genres = _context.SeriesMetadata
|
||||||
.Where(sm => seriesIds.Contains(sm.SeriesId))
|
.Where(sm => seriesIds.Contains(sm.SeriesId))
|
||||||
.SelectMany(sm => sm.Genres.Where(t => EF.Functions.Like(t.Title, $"%{searchQuery}%")))
|
.SelectMany(sm => sm.Genres.Where(t => EF.Functions.Like(t.Title, $"%{searchQuery}%")))
|
||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
.OrderBy(t => t.Title)
|
|
||||||
.Distinct()
|
.Distinct()
|
||||||
|
.OrderBy(t => t.Title)
|
||||||
.Take(maxRecords)
|
.Take(maxRecords)
|
||||||
.ProjectTo<GenreTagDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<GenreTagDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.AsEnumerable();
|
||||||
|
|
||||||
result.Tags = await _context.SeriesMetadata
|
result.Tags = _context.SeriesMetadata
|
||||||
.Where(sm => seriesIds.Contains(sm.SeriesId))
|
.Where(sm => seriesIds.Contains(sm.SeriesId))
|
||||||
.SelectMany(sm => sm.Tags.Where(t => EF.Functions.Like(t.Title, $"%{searchQuery}%")))
|
.SelectMany(sm => sm.Tags.Where(t => EF.Functions.Like(t.Title, $"%{searchQuery}%")))
|
||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
.OrderBy(t => t.Title)
|
|
||||||
.Distinct()
|
.Distinct()
|
||||||
|
.OrderBy(t => t.Title)
|
||||||
.Take(maxRecords)
|
.Take(maxRecords)
|
||||||
.ProjectTo<TagDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<TagDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.AsEnumerable();
|
||||||
|
|
||||||
var fileIds = _context.Series
|
var fileIds = _context.Series
|
||||||
.Where(s => seriesIds.Contains(s.Id))
|
.Where(s => seriesIds.Contains(s.Id))
|
||||||
|
|
@ -387,22 +387,24 @@ public class SeriesRepository : ISeriesRepository
|
||||||
.SelectMany(v => v.Chapters)
|
.SelectMany(v => v.Chapters)
|
||||||
.SelectMany(c => c.Files.Select(f => f.Id));
|
.SelectMany(c => c.Files.Select(f => f.Id));
|
||||||
|
|
||||||
result.Files = await _context.MangaFile
|
result.Files = _context.MangaFile
|
||||||
.Where(m => EF.Functions.Like(m.FilePath, $"%{searchQuery}%") && fileIds.Contains(m.Id))
|
.Where(m => EF.Functions.Like(m.FilePath, $"%{searchQuery}%") && fileIds.Contains(m.Id))
|
||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
|
.OrderBy(f => f.Id)
|
||||||
.Take(maxRecords)
|
.Take(maxRecords)
|
||||||
.ProjectTo<MangaFileDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<MangaFileDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.AsEnumerable();
|
||||||
|
|
||||||
|
|
||||||
result.Chapters = await _context.Chapter
|
result.Chapters = _context.Chapter
|
||||||
.Include(c => c.Files)
|
.Include(c => c.Files)
|
||||||
.Where(c => EF.Functions.Like(c.TitleName, $"%{searchQuery}%"))
|
.Where(c => EF.Functions.Like(c.TitleName, $"%{searchQuery}%"))
|
||||||
.Where(c => c.Files.All(f => fileIds.Contains(f.Id)))
|
.Where(c => c.Files.All(f => fileIds.Contains(f.Id)))
|
||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
|
.OrderBy(c => c.Id)
|
||||||
.Take(maxRecords)
|
.Take(maxRecords)
|
||||||
.ProjectTo<ChapterDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<ChapterDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.AsEnumerable();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue