mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-10-31 02:27:11 -04:00 
			
		
		
		
	Implementing a basic query parser for where, sort & pagination
This commit is contained in:
		
							parent
							
								
									3443e102e9
								
							
						
					
					
						commit
						ea625fa45b
					
				| @ -31,6 +31,27 @@ namespace Kyoo.Controllers | |||||||
| 			Key = key; | 			Key = key; | ||||||
| 			Descendant = descendant; | 			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<Func<T, object>>(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<T> : IDisposable, IAsyncDisposable | 	public interface IRepository<T> : IDisposable, IAsyncDisposable | ||||||
|  | |||||||
| @ -236,5 +236,49 @@ namespace Kyoo | |||||||
| 			 | 			 | ||||||
| 			return member.Member.Name; | 			return member.Member.Name; | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		public static Expression<Func<T, bool>> ParseWhere<T>(Dictionary<string, string> where) | ||||||
|  | 		{ | ||||||
|  | 			if (where == null || where.Count == 0) | ||||||
|  | 				return null; | ||||||
|  | 			 | ||||||
|  | 			ParameterExpression param = Expression.Parameter(typeof(T)); | ||||||
|  | 			Expression expression = null; | ||||||
|  | 
 | ||||||
|  | 			foreach (KeyValuePair<string, string> 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<Func<T, bool>>(expression!); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -75,7 +75,25 @@ namespace Kyoo.Controllers | |||||||
| 			Sort<Show> sort = default, | 			Sort<Show> sort = default, | ||||||
| 			Pagination page = default) | 			Pagination page = default) | ||||||
| 		{ | 		{ | ||||||
| 			return await _database.Shows.ToListAsync(); | 			IQueryable<Show> query = _database.Shows; | ||||||
|  | 
 | ||||||
|  | 			if (where != null) | ||||||
|  | 				query = query.Where(where); | ||||||
|  | 
 | ||||||
|  | 			Expression<Func<Show, object>> 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<Func<Show, bool>>( | ||||||
|  | 					Expression.GreaterThan(sortKey, Expression.Constant(afterObj)) | ||||||
|  | 				)); | ||||||
|  | 			} | ||||||
|  | 			query = query.Take(page.Count <= 0 ? 20 : page.Count); | ||||||
|  | 
 | ||||||
|  | 			return await query.ToListAsync(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public async Task<int> Create(Show obj) | 		public async Task<int> Create(Show obj) | ||||||
|  | |||||||
| @ -1,8 +1,11 @@ | |||||||
| using Kyoo.Models; | using System; | ||||||
|  | using Kyoo.Models; | ||||||
| using Microsoft.AspNetCore.Mvc; | using Microsoft.AspNetCore.Mvc; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Linq; | using System.Linq; | ||||||
|  | using System.Linq.Expressions; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
|  | using Castle.DynamicProxy.Generators.Emitters.SimpleAST; | ||||||
| using Kyoo.Controllers; | using Kyoo.Controllers; | ||||||
| using Microsoft.AspNetCore.Authorization; | using Microsoft.AspNetCore.Authorization; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
| @ -35,12 +38,14 @@ namespace Kyoo.Api | |||||||
| 
 | 
 | ||||||
| 		[HttpGet] | 		[HttpGet] | ||||||
| 		[Authorize(Policy="Read")] | 		[Authorize(Policy="Read")] | ||||||
| 		public IEnumerable<Show> GetShows() | 		public async Task<IEnumerable<Show>> GetShows([FromQuery] string sortBy,  | ||||||
|  | 			[FromQuery] int limit,  | ||||||
|  | 			[FromQuery] int afterID, | ||||||
|  | 			[FromQuery] Dictionary<string, string> where) | ||||||
| 		{ | 		{ | ||||||
| 			return _database.LibraryLinks | 			return await _libraryManager.GetShows(Utility.ParseWhere<Show>(where), | ||||||
| 				.Include(x => x.Show) | 				new Sort<Show>(sortBy), | ||||||
| 				.Include(x => x.Collection) | 				new Pagination(limit, afterID)); | ||||||
| 				.AsEnumerable().Select(x => x.Show ?? x.Collection.AsShow()).ToList(); |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		[HttpGet("{slug}")] | 		[HttpGet("{slug}")] | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user