diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs index 1bf3fafc..d46f32e5 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs @@ -170,7 +170,27 @@ namespace Kyoo.Abstractions.Models /// The previous episode that should be seen before viewing this one. /// [Projectable(UseMemberBody = nameof(_PreviousEpisode), OnlyOnInclude = true)] - [LoadableRelation] public Episode? PreviousEpisode { get; set; } + [LoadableRelation( + // language=PostgreSQL + Sql = """ + select + "pe".*, + from + episodes as "pe" + where + "pe".show_id = "this".show_id + and ("pe".absolute_number < "this".absolute_number + or "pe".season_number < "this".season_number + or ("pe".season_number = "this".season_number + and e.episode_number < "this".episode_number)) + order by + "pe".absolute_number desc, + "pe".season_number desc, + "pe".episode_number desc + limit 1 + """ + )] + public Episode? PreviousEpisode { get; set; } private Episode? _PreviousEpisode => Show!.Episodes! .OrderByDescending(x => x.AbsoluteNumber) @@ -186,7 +206,27 @@ namespace Kyoo.Abstractions.Models /// The next episode to watch after this one. /// [Projectable(UseMemberBody = nameof(_NextEpisode), OnlyOnInclude = true)] - [LoadableRelation] public Episode? NextEpisode { get; set; } + [LoadableRelation( + // language=PostgreSQL + Sql = """ + select + "ne".*, + from + episodes as "ne" + where + "ne".show_id = "this".show_id + and ("ne".absolute_number > "this".absolute_number + or "ne".season_number > "this".season_number + or ("ne".season_number = "this".season_number + and e.episode_number > "this".episode_number)) + order by + "ne".absolute_number, + "ne".season_number, + "ne".episode_number + limit 1 + """ + )] + public Episode? NextEpisode { get; set; } private Episode? _NextEpisode => Show!.Episodes! .OrderBy(x => x.AbsoluteNumber) diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs index 9ad9cf00..2fdcfaa2 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs @@ -156,17 +156,17 @@ namespace Kyoo.Abstractions.Models // language=PostgreSQL Sql = """ select - fe.* + "fe".* from ( select e.*, row_number() over (partition by e.show_id order by e.absolute_number, e.season_number, e.episode_number) as number from - episodes as e) as fe + episodes as e) as "fe" where - fe.number <= 1 + "fe".number <= 1 """, - On = "show_id" + On = "show_id = \"this\".id" )] public Episode? FirstEpisode { get; set; } diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Include.cs b/back/src/Kyoo.Abstractions/Models/Utils/Include.cs index e434e64c..8b7c49e9 100644 --- a/back/src/Kyoo.Abstractions/Models/Utils/Include.cs +++ b/back/src/Kyoo.Abstractions/Models/Utils/Include.cs @@ -77,7 +77,7 @@ public class Include // prop.PropertyType.GetElementType() ?? prop.PropertyType.GenericTypeArguments.First() // ); // } - if (attr.Sql != null && attr.On != null) + if (attr.Sql != null) return new CustomRelation(prop.Name, prop.PropertyType, attr.Sql, attr.On, prop.DeclaringType!); throw new NotImplementedException(); }) @@ -90,5 +90,5 @@ public class Include public record SingleRelation(string Name, Type type, string RelationIdName) : Metadata(Name); - public record CustomRelation(string Name, Type type, string Sql, string On, Type Declaring) : Metadata(Name); + public record CustomRelation(string Name, Type type, string Sql, string? On, Type Declaring) : Metadata(Name); } diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs index f150a47d..4fa4c90b 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs @@ -147,8 +147,11 @@ namespace Kyoo.Core.Controllers break; case Include.CustomRelation(var name, var type, var sql, var on, var declaring): string owner = config.First(x => x.Value == declaring).Key; + string lateral = sql.Contains("\"this\"") ? " lateral" : string.Empty; + sql = sql.Replace("\"this\"", owner); + on = on?.Replace("\"this\"", owner); retConfig.Add($"r{relation}", type); - join.AppendLine($"left join ({sql}) as r{relation} on r{relation}.{on} = {owner}.id"); + join.AppendLine($"left join{lateral} ({sql}) as r{relation} on r{relation}.{on}"); break; default: throw new NotImplementedException();