Expanded People architecture and fixed migration

This commit is contained in:
JPVenson
2024-10-11 11:11:15 +00:00
parent f397fc5b98
commit b73985e04f
16 changed files with 3711 additions and 103 deletions

View File

@@ -81,7 +81,8 @@ public sealed class BaseItemRepository(
using var context = dbProvider.CreateDbContext();
using var transaction = context.Database.BeginTransaction();
context.Peoples.Where(e => e.ItemId == id).ExecuteDelete();
context.PeopleBaseItemMap.Where(e => e.ItemId == id).ExecuteDelete();
context.Peoples.Where(e => e.BaseItems!.Count == 0).ExecuteDelete();
context.Chapters.Where(e => e.ItemId == id).ExecuteDelete();
context.MediaStreamInfos.Where(e => e.ItemId == id).ExecuteDelete();
context.AncestorIds.Where(e => e.ItemId == id).ExecuteDelete();
@@ -602,13 +603,13 @@ public sealed class BaseItemRepository(
{
baseQuery = baseQuery
.Where(e =>
context.Peoples.Where(w => context.BaseItems.Where(r => filter.PersonIds.Contains(r.Id)).Any(f => f.Name == w.Name))
context.PeopleBaseItemMap.Where(w => context.BaseItems.Where(r => filter.PersonIds.Contains(r.Id)).Any(f => f.Name == w.People.Name))
.Any(f => f.ItemId == e.Id));
}
if (!string.IsNullOrWhiteSpace(filter.Person))
{
baseQuery = baseQuery.Where(e => e.Peoples!.Any(f => f.Name == filter.Person));
baseQuery = baseQuery.Where(e => e.Peoples!.Any(f => f.People.Name == filter.Person));
}
if (!string.IsNullOrWhiteSpace(filter.MinSortName))
@@ -934,7 +935,7 @@ public sealed class BaseItemRepository(
if (filter.IsDeadPerson.HasValue && filter.IsDeadPerson.Value)
{
baseQuery = baseQuery
.Where(e => !e.Peoples!.Any(f => f.Name == e.Name));
.Where(e => !e.Peoples!.Any(f => f.People.Name == e.Name));
}
if (filter.Years.Length == 1)

View File

@@ -10,6 +10,7 @@ using MediaBrowser.Controller.Persistence;
using Microsoft.EntityFrameworkCore;
namespace Jellyfin.Server.Implementations.Item;
#pragma warning disable RS0030 // Do not use banned APIs
/// <summary>
/// Manager for handling people.
@@ -28,7 +29,7 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider) :
using var context = _dbProvider.CreateDbContext();
var dbQuery = TranslateQuery(context.Peoples.AsNoTracking(), context, filter);
dbQuery = dbQuery.OrderBy(e => e.ListOrder);
// dbQuery = dbQuery.OrderBy(e => e.ListOrder);
if (filter.Limit > 0)
{
dbQuery = dbQuery.Take(filter.Limit);
@@ -43,7 +44,7 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider) :
using var context = _dbProvider.CreateDbContext();
var dbQuery = TranslateQuery(context.Peoples.AsNoTracking(), context, filter);
dbQuery = dbQuery.OrderBy(e => e.ListOrder);
// dbQuery = dbQuery.OrderBy(e => e.ListOrder);
if (filter.Limit > 0)
{
dbQuery = dbQuery.Take(filter.Limit);
@@ -58,7 +59,29 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider) :
using var context = _dbProvider.CreateDbContext();
using var transaction = context.Database.BeginTransaction();
context.Peoples.Where(e => e.ItemId.Equals(itemId)).ExecuteDelete();
context.PeopleBaseItemMap.Where(e => e.ItemId == itemId).ExecuteDelete();
foreach (var item in people)
{
var personEntity = Map(item);
var existingEntity = context.Peoples.FirstOrDefault(e => e.Id == personEntity.Id);
if (existingEntity is null)
{
context.Peoples.Add(personEntity);
existingEntity = personEntity;
}
context.PeopleBaseItemMap.Add(new PeopleBaseItemMap()
{
Item = null!,
ItemId = itemId,
People = existingEntity,
PeopleId = existingEntity.Id,
ListOrder = item.SortOrder,
SortOrder = item.SortOrder,
Role = item.Role
});
}
context.Peoples.AddRange(people.Select(Map));
context.SaveChanges();
transaction.Commit();
@@ -68,10 +91,8 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider) :
{
var personInfo = new PersonInfo()
{
ItemId = people.ItemId,
Id = people.Id,
Name = people.Name,
Role = people.Role,
SortOrder = people.SortOrder,
};
if (Enum.TryParse<PersonKind>(people.PersonType, out var kind))
{
@@ -85,13 +106,9 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider) :
{
var personInfo = new People()
{
ItemId = people.ItemId,
Name = people.Name,
Role = people.Role,
SortOrder = people.SortOrder,
PersonType = people.Type.ToString(),
Item = null!,
ListOrder = people.SortOrder
Id = people.Id,
};
return personInfo;
@@ -108,12 +125,12 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider) :
if (!filter.ItemId.IsEmpty())
{
query = query.Where(e => e.ItemId.Equals(filter.ItemId));
query = query.Where(e => e.BaseItems!.Any(w => w.ItemId.Equals(filter.ItemId)));
}
if (!filter.AppearsInItemId.IsEmpty())
{
query = query.Where(e => context.Peoples.Where(f => f.ItemId.Equals(filter.AppearsInItemId)).Select(e => e.Name).Contains(e.Name));
query = query.Where(e => e.BaseItems!.Any(w => w.ItemId.Equals(filter.AppearsInItemId)));
}
var queryPersonTypes = filter.PersonTypes.Where(IsValidPersonType).ToList();
@@ -129,9 +146,9 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider) :
query = query.Where(e => !queryPersonTypes.Contains(e.PersonType));
}
if (filter.MaxListOrder.HasValue)
if (filter.MaxListOrder.HasValue && !filter.ItemId.IsEmpty())
{
query = query.Where(e => e.ListOrder <= filter.MaxListOrder.Value);
query = query.Where(e => e.BaseItems!.First(w => w.ItemId == filter.ItemId).ListOrder <= filter.MaxListOrder.Value);
}
if (!string.IsNullOrWhiteSpace(filter.NameContains))

View File

@@ -112,7 +112,7 @@ public class JellyfinDbContext(DbContextOptions<JellyfinDbContext> options, ILog
public DbSet<Chapter> Chapters => Set<Chapter>();
/// <summary>
/// Gets the <see cref="DbSet{TEntity}"/> containing the user data.
/// Gets the <see cref="DbSet{TEntity}"/>.
/// </summary>
public DbSet<ItemValue> ItemValues => Set<ItemValue>();
@@ -122,15 +122,20 @@ public class JellyfinDbContext(DbContextOptions<JellyfinDbContext> options, ILog
public DbSet<ItemValueMap> ItemValuesMap => Set<ItemValueMap>();
/// <summary>
/// Gets the <see cref="DbSet{TEntity}"/> containing the user data.
/// Gets the <see cref="DbSet{TEntity}"/>.
/// </summary>
public DbSet<MediaStreamInfo> MediaStreamInfos => Set<MediaStreamInfo>();
/// <summary>
/// Gets the <see cref="DbSet{TEntity}"/> containing the user data.
/// Gets the <see cref="DbSet{TEntity}"/>.
/// </summary>
public DbSet<People> Peoples => Set<People>();
/// <summary>
/// Gets the <see cref="DbSet{TEntity}"/>.
/// </summary>
public DbSet<PeopleBaseItemMap> PeopleBaseItemMap => Set<PeopleBaseItemMap>();
/// <summary>
/// Gets the <see cref="DbSet{TEntity}"/> containing the referenced Providers with ids.
/// </summary>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
using System;
using System.Runtime.CompilerServices;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Jellyfin.Server.Implementations.Migrations
{
/// <inheritdoc />
public partial class LibraryPeopleMigration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Peoples_BaseItems_ItemId",
table: "Peoples");
migrationBuilder.DropPrimaryKey(
name: "PK_Peoples",
table: "Peoples");
migrationBuilder.DropIndex(
name: "IX_Peoples_ItemId_ListOrder",
table: "Peoples");
migrationBuilder.DropColumn(
name: "ListOrder",
table: "Peoples");
migrationBuilder.DropColumn(
name: "SortOrder",
table: "Peoples");
migrationBuilder.RenameColumn(
name: "ItemId",
table: "Peoples",
newName: "Id");
migrationBuilder.AlterColumn<string>(
name: "Role",
table: "Peoples",
type: "TEXT",
nullable: true,
oldClrType: typeof(string),
oldType: "TEXT");
migrationBuilder.AddPrimaryKey(
name: "PK_Peoples",
table: "Peoples",
column: "Id");
migrationBuilder.CreateTable(
name: "PeopleBaseItemMap",
columns: table => new
{
ItemId = table.Column<Guid>(type: "TEXT", nullable: false),
PeopleId = table.Column<Guid>(type: "TEXT", nullable: false),
SortOrder = table.Column<int>(type: "INTEGER", nullable: true),
ListOrder = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PeopleBaseItemMap", x => new { x.ItemId, x.PeopleId });
table.ForeignKey(
name: "FK_PeopleBaseItemMap_BaseItems_ItemId",
column: x => x.ItemId,
principalTable: "BaseItems",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PeopleBaseItemMap_Peoples_PeopleId",
column: x => x.PeopleId,
principalTable: "Peoples",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_PeopleBaseItemMap_ItemId_ListOrder",
table: "PeopleBaseItemMap",
columns: new[] { "ItemId", "ListOrder" });
migrationBuilder.CreateIndex(
name: "IX_PeopleBaseItemMap_ItemId_SortOrder",
table: "PeopleBaseItemMap",
columns: new[] { "ItemId", "SortOrder" });
migrationBuilder.CreateIndex(
name: "IX_PeopleBaseItemMap_PeopleId",
table: "PeopleBaseItemMap",
column: "PeopleId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PeopleBaseItemMap");
migrationBuilder.DropPrimaryKey(
name: "PK_Peoples",
table: "Peoples");
migrationBuilder.RenameColumn(
name: "Id",
table: "Peoples",
newName: "ItemId");
migrationBuilder.AlterColumn<string>(
name: "Role",
table: "Peoples",
type: "TEXT",
nullable: false,
defaultValue: string.Empty,
oldClrType: typeof(string),
oldType: "TEXT",
oldNullable: true);
migrationBuilder.AddColumn<int>(
name: "ListOrder",
table: "Peoples",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "SortOrder",
table: "Peoples",
type: "INTEGER",
nullable: true);
migrationBuilder.AddPrimaryKey(
name: "PK_Peoples",
table: "Peoples",
columns: new[] { "ItemId", "Role", "ListOrder" });
migrationBuilder.CreateIndex(
name: "IX_Peoples_ItemId_ListOrder",
table: "Peoples",
columns: new[] { "ItemId", "ListOrder" });
migrationBuilder.AddForeignKey(
name: "FK_Peoples_BaseItems_ItemId",
table: "Peoples",
column: "ItemId",
principalTable: "BaseItems",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Jellyfin.Server.Implementations.Migrations
{
/// <inheritdoc />
public partial class LibraryPeopleRoleMigration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Role",
table: "Peoples");
migrationBuilder.AddColumn<string>(
name: "Role",
table: "PeopleBaseItemMap",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Role",
table: "PeopleBaseItemMap");
migrationBuilder.AddColumn<string>(
name: "Role",
table: "Peoples",
type: "TEXT",
nullable: true);
}
}
}

View File

@@ -911,15 +911,10 @@ namespace Jellyfin.Server.Implementations.Migrations
modelBuilder.Entity("Jellyfin.Data.Entities.People", b =>
{
b.Property<Guid>("ItemId")
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Role")
.HasColumnType("TEXT");
b.Property<int?>("ListOrder")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
@@ -927,16 +922,39 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Property<string>("PersonType")
.HasColumnType("TEXT");
b.Property<int?>("SortOrder")
.HasColumnType("INTEGER");
b.HasKey("ItemId", "Role", "ListOrder");
b.HasKey("Id");
b.HasIndex("Name");
b.ToTable("Peoples");
});
modelBuilder.Entity("Jellyfin.Data.Entities.PeopleBaseItemMap", b =>
{
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<Guid>("PeopleId")
.HasColumnType("TEXT");
b.Property<int?>("ListOrder")
.HasColumnType("INTEGER");
b.Property<string>("Role")
.HasColumnType("TEXT");
b.Property<int?>("SortOrder")
.HasColumnType("INTEGER");
b.HasKey("ItemId", "PeopleId");
b.HasIndex("PeopleId");
b.HasIndex("ItemId", "ListOrder");
b.ToTable("Peoples");
b.HasIndex("ItemId", "SortOrder");
b.ToTable("PeopleBaseItemMap");
});
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
@@ -1473,7 +1491,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Navigation("Item");
});
modelBuilder.Entity("Jellyfin.Data.Entities.People", b =>
modelBuilder.Entity("Jellyfin.Data.Entities.PeopleBaseItemMap", b =>
{
b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Item")
.WithMany("Peoples")
@@ -1481,7 +1499,15 @@ namespace Jellyfin.Server.Implementations.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Jellyfin.Data.Entities.People", "People")
.WithMany("BaseItems")
.HasForeignKey("PeopleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
b.Navigation("People");
});
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
@@ -1559,6 +1585,11 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Navigation("BaseItemsMap");
});
modelBuilder.Entity("Jellyfin.Data.Entities.People", b =>
{
b.Navigation("BaseItems");
});
modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
{
b.Navigation("AccessSchedules");

View File

@@ -0,0 +1,22 @@
using System;
using Jellyfin.Data.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Jellyfin.Server.Implementations.ModelConfiguration;
/// <summary>
/// People configuration.
/// </summary>
public class PeopleBaseItemMapConfiguration : IEntityTypeConfiguration<PeopleBaseItemMap>
{
/// <inheritdoc/>
public void Configure(EntityTypeBuilder<PeopleBaseItemMap> builder)
{
builder.HasKey(e => new { e.ItemId, e.PeopleId });
builder.HasIndex(e => new { e.ItemId, e.SortOrder });
builder.HasIndex(e => new { e.ItemId, e.ListOrder });
builder.HasOne(e => e.Item);
builder.HasOne(e => e.People);
}
}

View File

@@ -13,8 +13,8 @@ public class PeopleConfiguration : IEntityTypeConfiguration<People>
/// <inheritdoc/>
public void Configure(EntityTypeBuilder<People> builder)
{
builder.HasKey(e => new { e.ItemId, e.Role, e.ListOrder });
builder.HasIndex(e => new { e.ItemId, e.ListOrder });
builder.HasKey(e => e.Id);
builder.HasIndex(e => e.Name);
builder.HasMany(e => e.BaseItems);
}
}