diff --git a/Kyoo.Common/Controllers/IRepository.cs b/Kyoo.Common/Controllers/IRepository.cs index 5f9fe3ae..9ceb0bd7 100644 --- a/Kyoo.Common/Controllers/IRepository.cs +++ b/Kyoo.Common/Controllers/IRepository.cs @@ -31,6 +31,27 @@ namespace Kyoo.Controllers Key = key; Descendant = descendant; } + + public Sort(string sortBy) + { + if (string.IsNullOrEmpty(sortBy)) + { + Key = null; + Descendant = false; + return; + } + + string key = sortBy.Contains(':') ? sortBy.Substring(0, sortBy.IndexOf(':')) : sortBy; + string order = sortBy.Contains(':') ? sortBy.Substring(sortBy.IndexOf(':') + 1) : null; + + Key = Expression.Lambda>(Expression.Property(Expression.Parameter(typeof(T), "x"), key)); + Descendant = order switch + { + "desc" => true, + "asc" => false, + _ => throw new ArgumentException($"The sort order, if set, should be :asc or :desc but it was :{order}.") + }; + } } public interface IRepository : IDisposable, IAsyncDisposable diff --git a/Kyoo.Common/Utility.cs b/Kyoo.Common/Utility.cs index 23313077..904427f3 100644 --- a/Kyoo.Common/Utility.cs +++ b/Kyoo.Common/Utility.cs @@ -236,5 +236,49 @@ namespace Kyoo return member.Member.Name; } + + public static Expression> ParseWhere(Dictionary where) + { + if (where == null || where.Count == 0) + return null; + + ParameterExpression param = Expression.Parameter(typeof(T)); + Expression expression = null; + + foreach (KeyValuePair cond in where) + { + string value = cond.Value; + string operand = "eq"; + if (value.Contains(':')) + { + operand = value.Substring(0, value.IndexOf(':')); + value = value.Substring(value.IndexOf(':') + 1); + } + + PropertyInfo valueParam = typeof(T).GetProperty(cond.Key); // TODO get this property with case insensitive. + // TODO throw if valueParam is null. + MemberExpression property = Expression.Property(param, valueParam); + ConstantExpression condValue = Expression.Constant(value); // TODO Cast this to the right type (take nullable into account). + + Expression condition = operand switch + { + "eq" => Expression.Equal(property, condValue), + "not" => Expression.NotEqual(property, condValue), + "lt" => Expression.LessThan(property, condValue), + "lte" => Expression.LessThanOrEqual(property, condValue), + "gt" => Expression.GreaterThan(property, condValue), + "gte" => Expression.GreaterThanOrEqual(property, condValue), + "like" => throw new NotImplementedException("Like not implemented yet"), + _ => throw new ArgumentException($"Invalid operand: {operand}") + }; + + if (expression != null) + expression = Expression.AndAlso(expression, condition); + else + expression = condition; + } + + return Expression.Lambda>(expression!); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/ShowRepository.cs b/Kyoo/Controllers/Repositories/ShowRepository.cs index 378c724c..4ac185fa 100644 --- a/Kyoo/Controllers/Repositories/ShowRepository.cs +++ b/Kyoo/Controllers/Repositories/ShowRepository.cs @@ -75,7 +75,25 @@ namespace Kyoo.Controllers Sort sort = default, Pagination page = default) { - return await _database.Shows.ToListAsync(); + IQueryable query = _database.Shows; + + if (where != null) + query = query.Where(where); + + Expression> sortKey = sort.Key ?? (x => x.Title); + query = sort.Descendant ? query.OrderByDescending(sortKey) : query.OrderBy(sortKey); + + if (page.AfterID != 0) + { + Show after = await Get(page.AfterID); + object afterObj = sortKey.Compile()(after); + query = query.Where(Expression.Lambda>( + Expression.GreaterThan(sortKey, Expression.Constant(afterObj)) + )); + } + query = query.Take(page.Count <= 0 ? 20 : page.Count); + + return await query.ToListAsync(); } public async Task Create(Show obj) diff --git a/Kyoo/Views/API/ShowsAPI.cs b/Kyoo/Views/API/ShowsAPI.cs index a22a485e..0ce7fa33 100644 --- a/Kyoo/Views/API/ShowsAPI.cs +++ b/Kyoo/Views/API/ShowsAPI.cs @@ -1,8 +1,11 @@ -using Kyoo.Models; +using System; +using Kyoo.Models; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; +using Castle.DynamicProxy.Generators.Emitters.SimpleAST; using Kyoo.Controllers; using Microsoft.AspNetCore.Authorization; using Microsoft.EntityFrameworkCore; @@ -35,12 +38,14 @@ namespace Kyoo.Api [HttpGet] [Authorize(Policy="Read")] - public IEnumerable GetShows() + public async Task> GetShows([FromQuery] string sortBy, + [FromQuery] int limit, + [FromQuery] int afterID, + [FromQuery] Dictionary where) { - return _database.LibraryLinks - .Include(x => x.Show) - .Include(x => x.Collection) - .AsEnumerable().Select(x => x.Show ?? x.Collection.AsShow()).ToList(); + return await _libraryManager.GetShows(Utility.ParseWhere(where), + new Sort(sortBy), + new Pagination(limit, afterID)); } [HttpGet("{slug}")]