mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 20:24:27 -04:00
Rework default sort and make it work with dapper
This commit is contained in:
parent
9ea177e2f6
commit
177391a74c
@ -26,7 +26,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
public interface ILibraryManager
|
public interface ILibraryManager
|
||||||
{
|
{
|
||||||
IRepository<T> Repository<T>()
|
IRepository<T> Repository<T>()
|
||||||
where T : class, IResource;
|
where T : class, IResource, IQuery;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The repository that handle libraries items (a wrapper around shows and collections).
|
/// The repository that handle libraries items (a wrapper around shows and collections).
|
||||||
|
@ -31,7 +31,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The resource's type that this repository manage.</typeparam>
|
/// <typeparam name="T">The resource's type that this repository manage.</typeparam>
|
||||||
public interface IRepository<T> : IBaseRepository
|
public interface IRepository<T> : IBaseRepository
|
||||||
where T : class, IResource
|
where T : class, IResource, IQuery
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The event handler type for all events of this repository.
|
/// The event handler type for all events of this repository.
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Models;
|
namespace Kyoo.Abstractions.Models;
|
||||||
@ -24,4 +25,7 @@ namespace Kyoo.Abstractions.Models;
|
|||||||
/// A show, a movie or a collection.
|
/// A show, a movie or a collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[OneOf(Types = new[] { typeof(Show), typeof(Movie), typeof(Collection) })]
|
[OneOf(Types = new[] { typeof(Show), typeof(Movie), typeof(Collection) })]
|
||||||
public interface ILibraryItem : IResource, IThumbnails, IMetadata, IAddedDate { }
|
public interface ILibraryItem : IResource, IThumbnails, IMetadata, IAddedDate, IQuery
|
||||||
|
{
|
||||||
|
static Sort IQuery.DefaultSort => new Sort<ILibraryItem>.By(nameof(Movie.AirDate));
|
||||||
|
}
|
||||||
|
@ -41,7 +41,7 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A new item
|
/// A new item
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class News : IResource, IMetadata, IThumbnails, IAddedDate
|
public class News : IResource, IMetadata, IThumbnails, IAddedDate, IQuery
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -28,8 +29,10 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A class representing collections of <see cref="Show"/>.
|
/// A class representing collections of <see cref="Show"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Collection : IResource, IMetadata, IThumbnails, IAddedDate, ILibraryItem
|
public class Collection : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, ILibraryItem
|
||||||
{
|
{
|
||||||
|
public static Sort DefaultSort => new Sort<Collection>.By(nameof(Collection.Name));
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ using System.Linq;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using EntityFrameworkCore.Projectables;
|
using EntityFrameworkCore.Projectables;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Models
|
namespace Kyoo.Abstractions.Models
|
||||||
@ -30,8 +31,15 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A class to represent a single show's episode.
|
/// A class to represent a single show's episode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Episode : IResource, IMetadata, IThumbnails, IAddedDate
|
public class Episode : IQuery, IResource, IMetadata, IThumbnails, IAddedDate
|
||||||
{
|
{
|
||||||
|
// Use absolute numbers by default and fallback to season/episodes if it does not exists.
|
||||||
|
public static Sort DefaultSort => new Sort<Episode>.Conglomerate(
|
||||||
|
new Sort<Episode>.By(x => x.AbsoluteNumber),
|
||||||
|
new Sort<Episode>.By(x => x.SeasonNumber),
|
||||||
|
new Sort<Episode>.By(x => x.EpisodeNumber)
|
||||||
|
);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
// Kyoo - A portable and vast media library solution.
|
||||||
|
// Copyright (c) Kyoo.
|
||||||
|
//
|
||||||
|
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
// Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// Kyoo is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
|
|
||||||
|
namespace Kyoo.Abstractions.Models;
|
||||||
|
|
||||||
|
public interface IQuery
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The sorting that will be used when no user defined one is present.
|
||||||
|
/// </summary>
|
||||||
|
public static virtual Sort DefaultSort => throw new NotImplementedException();
|
||||||
|
}
|
@ -19,6 +19,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -28,8 +29,10 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A series or a movie.
|
/// A series or a movie.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Movie : IResource, IMetadata, IOnMerge, IThumbnails, IAddedDate, ILibraryItem
|
public class Movie : IQuery, IResource, IMetadata, IOnMerge, IThumbnails, IAddedDate, ILibraryItem
|
||||||
{
|
{
|
||||||
|
public static Sort DefaultSort => new Sort<Movie>.By(x => x.Name);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -27,8 +28,10 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// An actor, voice actor, writer, animator, somebody who worked on a <see cref="Show"/>.
|
/// An actor, voice actor, writer, animator, somebody who worked on a <see cref="Show"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class People : IResource, IMetadata, IThumbnails
|
public class People : IQuery, IResource, IMetadata, IThumbnails
|
||||||
{
|
{
|
||||||
|
public static Sort DefaultSort => new Sort<People>.By(x => x.Name);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using EntityFrameworkCore.Projectables;
|
using EntityFrameworkCore.Projectables;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Models
|
namespace Kyoo.Abstractions.Models
|
||||||
@ -30,8 +31,10 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A season of a <see cref="Show"/>.
|
/// A season of a <see cref="Show"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Season : IResource, IMetadata, IThumbnails, IAddedDate
|
public class Season : IQuery, IResource, IMetadata, IThumbnails, IAddedDate
|
||||||
{
|
{
|
||||||
|
public static Sort DefaultSort => new Sort<Season>.By(x => x.SeasonNumber);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using EntityFrameworkCore.Projectables;
|
using EntityFrameworkCore.Projectables;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -30,8 +31,10 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A series or a movie.
|
/// A series or a movie.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Show : IResource, IMetadata, IOnMerge, IThumbnails, IAddedDate, ILibraryItem
|
public class Show : IQuery, IResource, IMetadata, IOnMerge, IThumbnails, IAddedDate, ILibraryItem
|
||||||
{
|
{
|
||||||
|
public static Sort DefaultSort => new Sort<Show>.By(x => x.Name);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
@ -107,7 +110,8 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Trailer { get; set; }
|
public string? Trailer { get; set; }
|
||||||
|
|
||||||
[SerializeIgnore] public DateTime? AirDate => StartAir;
|
[SerializeIgnore]
|
||||||
|
public DateTime? AirDate => StartAir;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -27,8 +28,10 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A studio that make shows.
|
/// A studio that make shows.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Studio : IResource, IMetadata
|
public class Studio : IQuery, IResource, IMetadata
|
||||||
{
|
{
|
||||||
|
public static Sort DefaultSort => new Sort<Studio>.By(x => x.Name);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -28,8 +29,10 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A single user of the app.
|
/// A single user of the app.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class User : IResource, IAddedDate
|
public class User : IQuery, IResource, IAddedDate
|
||||||
{
|
{
|
||||||
|
public static Sort DefaultSort => new Sort<User>.By(x => x.Username);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
@ -43,11 +43,14 @@ public class Include<T>
|
|||||||
|
|
||||||
return new Include<T>
|
return new Include<T>
|
||||||
{
|
{
|
||||||
Fields = fields.Split(',').Select(x =>
|
Fields = fields.Split(',').Select(key =>
|
||||||
{
|
{
|
||||||
PropertyInfo? prop = typeof(T).GetProperty(x, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
|
Type[] types = typeof(T).GetCustomAttribute<OneOfAttribute>()?.Types ?? new[] { typeof(T) };
|
||||||
|
PropertyInfo? prop = types
|
||||||
|
.Select(x => x.GetProperty(key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance))
|
||||||
|
.FirstOrDefault();
|
||||||
if (prop?.GetCustomAttribute<LoadableRelationAttribute>() == null)
|
if (prop?.GetCustomAttribute<LoadableRelationAttribute>() == null)
|
||||||
throw new ValidationException($"No loadable relation with the name {x}.");
|
throw new ValidationException($"No loadable relation with the name {key}.");
|
||||||
return prop.Name;
|
return prop.Name;
|
||||||
}).ToArray()
|
}).ToArray()
|
||||||
};
|
};
|
||||||
|
@ -21,15 +21,20 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Controllers
|
namespace Kyoo.Abstractions.Controllers
|
||||||
{
|
{
|
||||||
|
public record Sort;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Information about how a query should be sorted. What factor should decide the sort and in which order.
|
/// Information about how a query should be sorted. What factor should decide the sort and in which order.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">For witch type this sort applies</typeparam>
|
/// <typeparam name="T">For witch type this sort applies</typeparam>
|
||||||
public record Sort<T>
|
public record Sort<T> : Sort
|
||||||
|
where T : IQuery
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sort by a specific key
|
/// Sort by a specific key
|
||||||
@ -61,7 +66,13 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
public record Random(uint seed) : Sort<T>;
|
public record Random(uint seed) : Sort<T>;
|
||||||
|
|
||||||
/// <summary>The default sort method for the given type.</summary>
|
/// <summary>The default sort method for the given type.</summary>
|
||||||
public record Default : Sort<T>;
|
public record Default : Sort<T>
|
||||||
|
{
|
||||||
|
public void Deconstruct(out Sort<T> value)
|
||||||
|
{
|
||||||
|
value = (Sort<T>)T.DefaultSort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="Sort{T}"/> instance from a key's name (case insensitive).
|
/// Create a new <see cref="Sort{T}"/> instance from a key's name (case insensitive).
|
||||||
@ -91,7 +102,11 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
null => false,
|
null => false,
|
||||||
_ => throw new ValidationException($"The sort order, if set, should be :asc or :desc but it was :{order}.")
|
_ => throw new ValidationException($"The sort order, if set, should be :asc or :desc but it was :{order}.")
|
||||||
};
|
};
|
||||||
PropertyInfo? property = typeof(T).GetProperty(key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
|
|
||||||
|
Type[] types = typeof(T).GetCustomAttribute<OneOfAttribute>()?.Types ?? new[] { typeof(T) };
|
||||||
|
PropertyInfo? property = types
|
||||||
|
.Select(x => x.GetProperty(key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance))
|
||||||
|
.FirstOrDefault();
|
||||||
if (property == null)
|
if (property == null)
|
||||||
throw new ValidationException("The given sort key is not valid.");
|
throw new ValidationException("The given sort key is not valid.");
|
||||||
return new By(property.Name, desendant);
|
return new By(property.Name, desendant);
|
||||||
|
@ -34,6 +34,69 @@ namespace Kyoo.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Utility
|
public static class Utility
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a string to snake case. Stollen from
|
||||||
|
/// https://github.com/efcore/EFCore.NamingConventions/blob/main/EFCore.NamingConventions/Internal/SnakeCaseNameRewriter.cs
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The string to convert.</param>
|
||||||
|
/// <returns>The string in snake case</returns>
|
||||||
|
public static string ToSnakeCase(this string name)
|
||||||
|
{
|
||||||
|
StringBuilder builder = new(name.Length + Math.Min(2, name.Length / 5));
|
||||||
|
UnicodeCategory? previousCategory = default;
|
||||||
|
|
||||||
|
for (int currentIndex = 0; currentIndex < name.Length; currentIndex++)
|
||||||
|
{
|
||||||
|
char currentChar = name[currentIndex];
|
||||||
|
if (currentChar == '_')
|
||||||
|
{
|
||||||
|
builder.Append('_');
|
||||||
|
previousCategory = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnicodeCategory currentCategory = char.GetUnicodeCategory(currentChar);
|
||||||
|
switch (currentCategory)
|
||||||
|
{
|
||||||
|
case UnicodeCategory.UppercaseLetter:
|
||||||
|
case UnicodeCategory.TitlecaseLetter:
|
||||||
|
if (previousCategory == UnicodeCategory.SpaceSeparator ||
|
||||||
|
previousCategory == UnicodeCategory.LowercaseLetter ||
|
||||||
|
(previousCategory != UnicodeCategory.DecimalDigitNumber &&
|
||||||
|
previousCategory != null &&
|
||||||
|
currentIndex > 0 &&
|
||||||
|
currentIndex + 1 < name.Length &&
|
||||||
|
char.IsLower(name[currentIndex + 1])))
|
||||||
|
{
|
||||||
|
builder.Append('_');
|
||||||
|
}
|
||||||
|
|
||||||
|
currentChar = char.ToLowerInvariant(currentChar);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UnicodeCategory.LowercaseLetter:
|
||||||
|
case UnicodeCategory.DecimalDigitNumber:
|
||||||
|
if (previousCategory == UnicodeCategory.SpaceSeparator)
|
||||||
|
{
|
||||||
|
builder.Append('_');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (previousCategory != null)
|
||||||
|
{
|
||||||
|
previousCategory = UnicodeCategory.SpaceSeparator;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(currentChar);
|
||||||
|
previousCategory = currentCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is the lambda expression a member (like x => x.Body).
|
/// Is the lambda expression a member (like x => x.Body).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -92,7 +92,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
public IRepository<User> Users { get; }
|
public IRepository<User> Users { get; }
|
||||||
|
|
||||||
public IRepository<T> Repository<T>()
|
public IRepository<T> Repository<T>()
|
||||||
where T : class, IResource
|
where T : class, IResource, IQuery
|
||||||
{
|
{
|
||||||
return (IRepository<T>)_repositories.First(x => x.RepositoryType == typeof(T));
|
return (IRepository<T>)_repositories.First(x => x.RepositoryType == typeof(T));
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
using System;
|
using System;
|
||||||
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 Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
@ -39,9 +38,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly DatabaseContext _database;
|
private readonly DatabaseContext _database;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override Sort<Collection> DefaultSort => new Sort<Collection>.By(nameof(Collection.Name));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="CollectionRepository"/>.
|
/// Create a new <see cref="CollectionRepository"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -56,11 +52,10 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<Collection>> Search(string query, Include<Collection>? include = default)
|
public override async Task<ICollection<Collection>> Search(string query, Include<Collection>? include = default)
|
||||||
{
|
{
|
||||||
return await Sort(
|
return await AddIncludes(_database.Collections, include)
|
||||||
AddIncludes(_database.Collections, include)
|
.Where(_database.Like<Collection>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
||||||
.Where(_database.Like<Collection>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
.Take(20)
|
||||||
.Take(20)
|
.ToListAsync();
|
||||||
).ToListAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -41,14 +41,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
|
|
||||||
private readonly IRepository<Show> _shows;
|
private readonly IRepository<Show> _shows;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
// Use absolute numbers by default and fallback to season/episodes if it does not exists.
|
|
||||||
protected override Sort<Episode> DefaultSort => new Sort<Episode>.Conglomerate(
|
|
||||||
new Sort<Episode>.By(x => x.AbsoluteNumber),
|
|
||||||
new Sort<Episode>.By(x => x.SeasonNumber),
|
|
||||||
new Sort<Episode>.By(x => x.EpisodeNumber)
|
|
||||||
);
|
|
||||||
|
|
||||||
static EpisodeRepository()
|
static EpisodeRepository()
|
||||||
{
|
{
|
||||||
// Edit episode slugs when the show's slug changes.
|
// Edit episode slugs when the show's slug changes.
|
||||||
@ -86,10 +78,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<Episode>> Search(string query, Include<Episode>? include = default)
|
public override async Task<ICollection<Episode>> Search(string query, Include<Episode>? include = default)
|
||||||
{
|
{
|
||||||
return await Sort(
|
return await AddIncludes(_database.Episodes, include)
|
||||||
AddIncludes(_database.Episodes, include)
|
.Where(_database.Like<Episode>(x => x.Name!, $"%{query}%"))
|
||||||
.Where(_database.Like<Episode>(x => x.Name!, $"%{query}%"))
|
|
||||||
)
|
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ using Kyoo.Abstractions.Controllers;
|
|||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Exceptions;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
|
using Kyoo.Utils;
|
||||||
|
|
||||||
namespace Kyoo.Core.Controllers
|
namespace Kyoo.Core.Controllers
|
||||||
{
|
{
|
||||||
@ -91,17 +92,30 @@ namespace Kyoo.Core.Controllers
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ProcessSort<T>(Sort<T> sort, string[] tables)
|
public string ProcessSort<T>(Sort<T> sort, Dictionary<string, Type> config)
|
||||||
|
where T : IQuery
|
||||||
{
|
{
|
||||||
return sort switch
|
string Property(string key)
|
||||||
{
|
{
|
||||||
// TODO: Implement default sort by
|
if (config.Count == 1)
|
||||||
Sort<T>.Default => $"coalesce({string.Join(", ", tables.Select(x => $"{x}.name"))})",
|
return $"{config.First()}.{key.ToSnakeCase()}";
|
||||||
Sort<T>.By(string key, bool desc) => $"coalesce({string.Join(", ", tables.Select(x => $"{x}.{key}"))}) {(desc ? "desc" : "asc")}",
|
|
||||||
Sort<T>.Random(var seed) => $"md5('{seed}' || coalesce({string.Join(", ", tables.Select(x => $"{x}.id"))}))",
|
IEnumerable<string> keys = config
|
||||||
Sort<T>.Conglomerate(var list) => string.Join(", ", list.Select(x => ProcessSort(x, tables))),
|
.Where(x => x.Value.GetProperty(key) != null)
|
||||||
|
.Select(x => $"{x.Key}.{key.ToSnakeCase()}");
|
||||||
|
return $"coalesce({string.Join(", ", keys)})";
|
||||||
|
}
|
||||||
|
|
||||||
|
string ret = sort switch
|
||||||
|
{
|
||||||
|
Sort<T>.Default(var value) => ProcessSort(value, config),
|
||||||
|
Sort<T>.By(string key, bool desc) => $"{Property(key)} {(desc ? "desc nulls last" : "asc")}",
|
||||||
|
Sort<T>.Random(var seed) => $"md5('{seed}' || {Property("id")})",
|
||||||
|
Sort<T>.Conglomerate(var list) => string.Join(", ", list.Select(x => ProcessSort(x, config))),
|
||||||
_ => throw new SwitchExpressionException(),
|
_ => throw new SwitchExpressionException(),
|
||||||
};
|
};
|
||||||
|
// always end query by an id sort.
|
||||||
|
return $"{ret}, {Property("id")} asc";
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ICollection<ILibraryItem>> GetAll(
|
public async Task<ICollection<ILibraryItem>> GetAll(
|
||||||
@ -110,6 +124,12 @@ namespace Kyoo.Core.Controllers
|
|||||||
Pagination? limit = null,
|
Pagination? limit = null,
|
||||||
Include<ILibraryItem>? include = null)
|
Include<ILibraryItem>? include = null)
|
||||||
{
|
{
|
||||||
|
Dictionary<string, Type> config = new()
|
||||||
|
{
|
||||||
|
{ "s", typeof(Show) },
|
||||||
|
{ "m", typeof(Movie) },
|
||||||
|
{ "c", typeof(Collection) }
|
||||||
|
};
|
||||||
// language=PostgreSQL
|
// language=PostgreSQL
|
||||||
IDapperSqlCommand query = _database.SqlBuilder($"""
|
IDapperSqlCommand query = _database.SqlBuilder($"""
|
||||||
select
|
select
|
||||||
@ -130,11 +150,11 @@ namespace Kyoo.Core.Controllers
|
|||||||
from
|
from
|
||||||
collections) as c on false
|
collections) as c on false
|
||||||
left join studios as st on st.id = coalesce(s.studio_id, m.studio_id)
|
left join studios as st on st.id = coalesce(s.studio_id, m.studio_id)
|
||||||
order by {ProcessSort(sort, new[] { "s", "m", "c" }):raw}
|
order by {ProcessSort(sort, config):raw}
|
||||||
limit {limit.Limit}
|
limit {limit.Limit}
|
||||||
""").Build();
|
""").Build();
|
||||||
|
|
||||||
Type[] types = new[] { typeof(Show), typeof(Movie), typeof(Collection), typeof(Studio) };
|
Type[] types = config.Select(x => x.Value).Concat(new[] { typeof(Studio) }).ToArray();
|
||||||
IEnumerable<ILibraryItem> data = await query.QueryAsync<ILibraryItem>(types, items =>
|
IEnumerable<ILibraryItem> data = await query.QueryAsync<ILibraryItem>(types, items =>
|
||||||
{
|
{
|
||||||
var studio = items[3] as Studio;
|
var studio = items[3] as Studio;
|
||||||
|
@ -40,7 +40,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of this repository</typeparam>
|
/// <typeparam name="T">The type of this repository</typeparam>
|
||||||
public abstract class LocalRepository<T> : IRepository<T>
|
public abstract class LocalRepository<T> : IRepository<T>
|
||||||
where T : class, IResource
|
where T : class, IResource, IQuery
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Entity Framework's Database handle.
|
/// The Entity Framework's Database handle.
|
||||||
@ -52,11 +52,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IThumbnailsManager _thumbs;
|
private readonly IThumbnailsManager _thumbs;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The default sort order that will be used for this resource's type.
|
|
||||||
/// </summary>
|
|
||||||
protected abstract Sort<T> DefaultSort { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new base <see cref="LocalRepository{T}"/> with the given database handle.
|
/// Create a new base <see cref="LocalRepository{T}"/> with the given database handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -77,9 +72,9 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <param name="query">The query to sort.</param>
|
/// <param name="query">The query to sort.</param>
|
||||||
/// <param name="sortBy">How to sort the query.</param>
|
/// <param name="sortBy">How to sort the query.</param>
|
||||||
/// <returns>The newly sorted query.</returns>
|
/// <returns>The newly sorted query.</returns>
|
||||||
protected IOrderedQueryable<T> Sort(IQueryable<T> query, Sort<T>? sortBy = null)
|
protected IOrderedQueryable<T> Sort(IQueryable<T> query, Sort<T>? sortBy)
|
||||||
{
|
{
|
||||||
sortBy ??= DefaultSort;
|
sortBy ??= new Sort<T>.Default();
|
||||||
|
|
||||||
IOrderedQueryable<T> _SortBy(IQueryable<T> qr, Expression<Func<T, object>> sort, bool desc, bool then)
|
IOrderedQueryable<T> _SortBy(IQueryable<T> qr, Expression<Func<T, object>> sort, bool desc, bool then)
|
||||||
{
|
{
|
||||||
@ -98,8 +93,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
switch (sortBy)
|
switch (sortBy)
|
||||||
{
|
{
|
||||||
case Sort<T>.Default:
|
case Sort<T>.Default(var value):
|
||||||
return _Sort(query, DefaultSort, then);
|
return _Sort(query, value, then);
|
||||||
case Sort<T>.By(var key, var desc):
|
case Sort<T>.By(var key, var desc):
|
||||||
return _SortBy(query, x => EF.Property<T>(x, key), desc, then);
|
return _SortBy(query, x => EF.Property<T>(x, key), desc, then);
|
||||||
case Sort<T>.Random(var seed):
|
case Sort<T>.Random(var seed):
|
||||||
@ -154,7 +149,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
T reference,
|
T reference,
|
||||||
bool next = true)
|
bool next = true)
|
||||||
{
|
{
|
||||||
sort ??= DefaultSort;
|
sort ??= new Sort<T>.Default();
|
||||||
|
|
||||||
// x =>
|
// x =>
|
||||||
ParameterExpression x = Expression.Parameter(typeof(T), "x");
|
ParameterExpression x = Expression.Parameter(typeof(T), "x");
|
||||||
@ -173,7 +168,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
return sort switch
|
return sort switch
|
||||||
{
|
{
|
||||||
Sort<T>.Default => GetSortsBy(DefaultSort),
|
Sort<T>.Default(var value) => GetSortsBy(value),
|
||||||
Sort<T>.By @sortBy => new[] { new SortIndicator(sortBy.Key, sortBy.Desendant, null) },
|
Sort<T>.By @sortBy => new[] { new SortIndicator(sortBy.Key, sortBy.Desendant, null) },
|
||||||
Sort<T>.Conglomerate(var list) => list.SelectMany(GetSortsBy),
|
Sort<T>.Conglomerate(var list) => list.SelectMany(GetSortsBy),
|
||||||
Sort<T>.Random(var seed) => new[] { new SortIndicator("random", false, seed.ToString()) },
|
Sort<T>.Random(var seed) => new[] { new SortIndicator("random", false, seed.ToString()) },
|
||||||
|
@ -47,9 +47,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IRepository<People> _people;
|
private readonly IRepository<People> _people;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override Sort<Movie> DefaultSort => new Sort<Movie>.By(x => x.Name);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="MovieRepository"/>.
|
/// Create a new <see cref="MovieRepository"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -71,10 +68,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<Movie>> Search(string query, Include<Movie>? include = default)
|
public override async Task<ICollection<Movie>> Search(string query, Include<Movie>? include = default)
|
||||||
{
|
{
|
||||||
return await Sort(
|
return await AddIncludes(_database.Movies, include)
|
||||||
AddIncludes(_database.Movies, include)
|
.Where(_database.Like<Movie>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
||||||
.Where(_database.Like<Movie>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
|
||||||
)
|
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NewsRepository : LocalRepository<News>
|
public class NewsRepository : LocalRepository<News>
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
|
||||||
protected override Sort<News> DefaultSort => new Sort<News>.By(x => x.AddedDate, true);
|
|
||||||
|
|
||||||
public NewsRepository(DatabaseContext database, IThumbnailsManager thumbs)
|
public NewsRepository(DatabaseContext database, IThumbnailsManager thumbs)
|
||||||
: base(database, thumbs)
|
: base(database, thumbs)
|
||||||
{ }
|
{ }
|
||||||
|
@ -44,9 +44,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Lazy<IRepository<Show>> _shows;
|
private readonly Lazy<IRepository<Show>> _shows;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override Sort<People> DefaultSort => new Sort<People>.By(x => x.Name);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="PeopleRepository"/>
|
/// Create a new <see cref="PeopleRepository"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -65,10 +62,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<People>> Search(string query, Include<People>? include = default)
|
public override async Task<ICollection<People>> Search(string query, Include<People>? include = default)
|
||||||
{
|
{
|
||||||
return await Sort(
|
return await AddIncludes(_database.People, include)
|
||||||
AddIncludes(_database.People, include)
|
.Where(_database.Like<People>(x => x.Name, $"%{query}%"))
|
||||||
.Where(_database.Like<People>(x => x.Name, $"%{query}%"))
|
|
||||||
)
|
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly DatabaseContext _database;
|
private readonly DatabaseContext _database;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override Sort<Season> DefaultSort => new Sort<Season>.By(x => x.SeasonNumber);
|
|
||||||
|
|
||||||
static SeasonRepository()
|
static SeasonRepository()
|
||||||
{
|
{
|
||||||
// Edit seasons slugs when the show's slug changes.
|
// Edit seasons slugs when the show's slug changes.
|
||||||
@ -76,10 +73,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task<ICollection<Season>> Search(string query, Include<Season>? include = default)
|
public override async Task<ICollection<Season>> Search(string query, Include<Season>? include = default)
|
||||||
{
|
{
|
||||||
return await Sort(
|
return await AddIncludes(_database.Seasons, include)
|
||||||
AddIncludes(_database.Seasons, include)
|
.Where(_database.Like<Season>(x => x.Name!, $"%{query}%"))
|
||||||
.Where(_database.Like<Season>(x => x.Name!, $"%{query}%"))
|
|
||||||
)
|
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IRepository<People> _people;
|
private readonly IRepository<People> _people;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override Sort<Show> DefaultSort => new Sort<Show>.By(x => x.Name);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="ShowRepository"/>.
|
/// Create a new <see cref="ShowRepository"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -72,10 +69,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<Show>> Search(string query, Include<Show>? include = default)
|
public override async Task<ICollection<Show>> Search(string query, Include<Show>? include = default)
|
||||||
{
|
{
|
||||||
return await Sort(
|
return await AddIncludes(_database.Shows, include)
|
||||||
AddIncludes(_database.Shows, include)
|
.Where(_database.Like<Show>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
||||||
.Where(_database.Like<Show>(x => x.Name + " " + x.Slug, $"%{query}%"))
|
|
||||||
)
|
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly DatabaseContext _database;
|
private readonly DatabaseContext _database;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override Sort<Studio> DefaultSort => new Sort<Studio>.By(x => x.Name);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="StudioRepository"/>.
|
/// Create a new <see cref="StudioRepository"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -55,10 +52,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<Studio>> Search(string query, Include<Studio>? include = default)
|
public override async Task<ICollection<Studio>> Search(string query, Include<Studio>? include = default)
|
||||||
{
|
{
|
||||||
return await Sort(
|
return await AddIncludes(_database.Studios, include)
|
||||||
AddIncludes(_database.Studios, include)
|
.Where(_database.Like<Studio>(x => x.Name, $"%{query}%"))
|
||||||
.Where(_database.Like<Studio>(x => x.Name, $"%{query}%"))
|
|
||||||
)
|
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly DatabaseContext _database;
|
private readonly DatabaseContext _database;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override Sort<User> DefaultSort => new Sort<User>.By(x => x.Username);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="UserRepository"/>
|
/// Create a new <see cref="UserRepository"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -54,10 +51,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<User>> Search(string query, Include<User>? include = default)
|
public override async Task<ICollection<User>> Search(string query, Include<User>? include = default)
|
||||||
{
|
{
|
||||||
return await Sort(
|
return await AddIncludes(_database.Users, include)
|
||||||
AddIncludes(_database.Users, include)
|
.Where(_database.Like<User>(x => x.Username, $"%{query}%"))
|
||||||
.Where(_database.Like<User>(x => x.Username, $"%{query}%"))
|
|
||||||
)
|
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ namespace Kyoo.Core.Api
|
|||||||
[ApiController]
|
[ApiController]
|
||||||
[ResourceView]
|
[ResourceView]
|
||||||
public class CrudApi<T> : BaseApi
|
public class CrudApi<T> : BaseApi
|
||||||
where T : class, IResource
|
where T : class, IResource, IQuery
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The repository of the resource, used to retrieve, save and do operations on the baking store.
|
/// The repository of the resource, used to retrieve, save and do operations on the baking store.
|
||||||
|
@ -36,7 +36,7 @@ namespace Kyoo.Core.Api
|
|||||||
[ApiController]
|
[ApiController]
|
||||||
[ResourceView]
|
[ResourceView]
|
||||||
public class CrudThumbsApi<T> : CrudApi<T>
|
public class CrudThumbsApi<T> : CrudApi<T>
|
||||||
where T : class, IResource, IThumbnails
|
where T : class, IResource, IThumbnails, IQuery
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The thumbnail manager used to retrieve images paths.
|
/// The thumbnail manager used to retrieve images paths.
|
||||||
|
@ -77,7 +77,7 @@ namespace Kyoo.Core.Api
|
|||||||
{
|
{
|
||||||
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||||
|
|
||||||
if (properties.All(x => x.PropertyName != "kind"))
|
if (properties.All(x => x.PropertyName != "kind") && type.IsAssignableTo(typeof(IResource)))
|
||||||
{
|
{
|
||||||
properties.Add(new JsonProperty()
|
properties.Add(new JsonProperty()
|
||||||
{
|
{
|
||||||
|
@ -20,6 +20,7 @@ using System;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
|
using Kyoo.Abstractions.Models;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ public class SortBinder : IModelBinder
|
|||||||
);
|
);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
object sort = bindingContext.ModelType.GetMethod(nameof(Sort<object>.From))!
|
object sort = bindingContext.ModelType.GetMethod(nameof(Sort<Movie>.From))!
|
||||||
.Invoke(null, new object?[] { sortBy.FirstValue, seed })!;
|
.Invoke(null, new object?[] { sortBy.FirstValue, seed })!;
|
||||||
bindingContext.Result = ModelBindingResult.Success(sort);
|
bindingContext.Result = ModelBindingResult.Success(sort);
|
||||||
bindingContext.HttpContext.Items["seed"] = seed;
|
bindingContext.HttpContext.Items["seed"] = seed;
|
||||||
|
@ -31,6 +31,7 @@ public class SearchManager : ISearchManager
|
|||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
private static IEnumerable<string> _GetSortsBy<T>(string index, Sort<T>? sort)
|
private static IEnumerable<string> _GetSortsBy<T>(string index, Sort<T>? sort)
|
||||||
|
where T : IQuery
|
||||||
{
|
{
|
||||||
return sort switch
|
return sort switch
|
||||||
{
|
{
|
||||||
@ -55,7 +56,7 @@ public class SearchManager : ISearchManager
|
|||||||
Sort<T>? sortBy = default,
|
Sort<T>? sortBy = default,
|
||||||
SearchPagination? pagination = default,
|
SearchPagination? pagination = default,
|
||||||
Include<T>? include = default)
|
Include<T>? include = default)
|
||||||
where T : class, IResource
|
where T : class, IResource, IQuery
|
||||||
{
|
{
|
||||||
// TODO: add filters and facets
|
// TODO: add filters and facets
|
||||||
ISearchable<IdResource> res = await _client.Index(index).SearchAsync<IdResource>(query, new SearchQuery()
|
ISearchable<IdResource> res = await _client.Index(index).SearchAsync<IdResource>(query, new SearchQuery()
|
||||||
|
@ -193,10 +193,6 @@ namespace Kyoo.Postgresql.Migrations
|
|||||||
.HasColumnType("genre[]")
|
.HasColumnType("genre[]")
|
||||||
.HasColumnName("genres");
|
.HasColumnName("genres");
|
||||||
|
|
||||||
b.Property<ItemKind>("Kind")
|
|
||||||
.HasColumnType("item_kind")
|
|
||||||
.HasColumnName("kind");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text")
|
.HasColumnType("text")
|
||||||
|
@ -194,10 +194,6 @@ namespace Kyoo.Postgresql.Migrations
|
|||||||
.HasColumnType("genre[]")
|
.HasColumnType("genre[]")
|
||||||
.HasColumnName("genres");
|
.HasColumnName("genres");
|
||||||
|
|
||||||
b.Property<ItemKind>("Kind")
|
|
||||||
.HasColumnType("item_kind")
|
|
||||||
.HasColumnName("kind");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text")
|
.HasColumnType("text")
|
||||||
|
@ -198,10 +198,6 @@ namespace Kyoo.Postgresql.Migrations
|
|||||||
.HasColumnType("genre[]")
|
.HasColumnType("genre[]")
|
||||||
.HasColumnName("genres");
|
.HasColumnName("genres");
|
||||||
|
|
||||||
b.Property<ItemKind>("Kind")
|
|
||||||
.HasColumnType("item_kind")
|
|
||||||
.HasColumnName("kind");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text")
|
.HasColumnType("text")
|
||||||
|
@ -195,10 +195,6 @@ namespace Kyoo.Postgresql.Migrations
|
|||||||
.HasColumnType("genre[]")
|
.HasColumnType("genre[]")
|
||||||
.HasColumnName("genres");
|
.HasColumnName("genres");
|
||||||
|
|
||||||
b.Property<ItemKind>("Kind")
|
|
||||||
.HasColumnType("item_kind")
|
|
||||||
.HasColumnName("kind");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text")
|
.HasColumnType("text")
|
||||||
|
@ -50,7 +50,6 @@ namespace Kyoo.Postgresql
|
|||||||
{
|
{
|
||||||
NpgsqlConnection.GlobalTypeMapper.MapEnum<Status>();
|
NpgsqlConnection.GlobalTypeMapper.MapEnum<Status>();
|
||||||
NpgsqlConnection.GlobalTypeMapper.MapEnum<Genre>();
|
NpgsqlConnection.GlobalTypeMapper.MapEnum<Genre>();
|
||||||
NpgsqlConnection.GlobalTypeMapper.MapEnum<ItemKind>();
|
|
||||||
NpgsqlConnection.GlobalTypeMapper.MapEnum<NewsKind>();
|
NpgsqlConnection.GlobalTypeMapper.MapEnum<NewsKind>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +103,6 @@ namespace Kyoo.Postgresql
|
|||||||
{
|
{
|
||||||
modelBuilder.HasPostgresEnum<Status>();
|
modelBuilder.HasPostgresEnum<Status>();
|
||||||
modelBuilder.HasPostgresEnum<Genre>();
|
modelBuilder.HasPostgresEnum<Genre>();
|
||||||
modelBuilder.HasPostgresEnum<ItemKind>();
|
|
||||||
modelBuilder.HasPostgresEnum<NewsKind>();
|
modelBuilder.HasPostgresEnum<NewsKind>();
|
||||||
|
|
||||||
modelBuilder.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(MD5))!)
|
modelBuilder.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(MD5))!)
|
||||||
|
@ -21,7 +21,6 @@ using System.Collections.Generic;
|
|||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using EFCore.NamingConventions.Internal;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Postgresql.Utils;
|
using Kyoo.Postgresql.Utils;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user