Attempt to remodel PhotoSubject and Photo relationship

This commit is contained in:
Slendy 2023-02-21 16:16:25 -06:00
commit 6cd3d61fa0
No known key found for this signature in database
GPG key ID: 7288D68361B91428
8 changed files with 126 additions and 40 deletions

View file

@ -95,20 +95,20 @@ public class PhotosController : ControllerBase
if (validLevel) photo.SlotId = photo.XmlLevelInfo.SlotId;
}
if (photo.Subjects.Count > 4) return this.BadRequest();
if (photo.PhotoSubjects.Count > 4) return this.BadRequest();
if (photo.Timestamp > TimeHelper.Timestamp) photo.Timestamp = TimeHelper.Timestamp;
// Check for duplicate photo subjects
List<string> subjectUserIds = new(4);
foreach (PhotoSubject subject in photo.Subjects)
foreach (PhotoSubject subject in photo.PhotoSubjects)
{
if (subjectUserIds.Contains(subject.Username) && !string.IsNullOrEmpty(subject.Username)) return this.BadRequest();
subjectUserIds.Add(subject.Username);
}
foreach (PhotoSubject subject in photo.Subjects.Where(subject => !string.IsNullOrEmpty(subject.Username)))
foreach (PhotoSubject subject in photo.PhotoSubjects.Where(subject => !string.IsNullOrEmpty(subject.Username)))
{
subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username);
@ -122,7 +122,7 @@ public class PhotosController : ControllerBase
await this.database.SaveChangesAsync();
photo.PhotoSubjectIds = photo.Subjects.Where(s => s.UserId != 0).Select(subject => subject.PhotoSubjectId.ToString()).ToArray();
photo.PhotoSubjectIds = photo.PhotoSubjects.Where(s => s.UserId != 0).Select(subject => subject.PhotoSubjectId.ToString()).ToArray();
Logger.Debug($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LogArea.Photos);

View file

@ -76,21 +76,21 @@
</i>
</p>
@if (Model.Subjects.Count > 0)
@if (Model.PhotoSubjects.Count > 0)
{
<p>
<b>Photo contains @Model.Subjects.Count @(Model.Subjects.Count == 1 ? "person" : "people"):</b>
<b>Photo contains @Model.PhotoSubjects.Count @(Model.PhotoSubjects.Count == 1 ? "person" : "people"):</b>
</p>
}
<div id="hover-subjects-@Model.PhotoId">
@foreach (PhotoSubject subject in Model.Subjects)
@foreach (PhotoSubject subject in Model.PhotoSubjects)
{
@await subject.User.ToLink(Html, ViewData, language, timeZone)
}
</div>
@{
PhotoSubject[] subjects = Model.Subjects.ToArray();
PhotoSubject[] subjects = Model.PhotoSubjects.ToArray();
foreach (PhotoSubject subject in subjects) subject.Username = subject.User.Username;
}

View file

@ -32,7 +32,7 @@ public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob
// Checks should generally be ordered in least computationally expensive to most.
if (photo.Subjects.Count > 4)
if (photo.PhotoSubjects.Count > 4)
{
tooManyPhotoSubjects = true;
goto removePhoto;
@ -60,7 +60,7 @@ public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob
};
List<int> subjectUserIds = new(4);
foreach (PhotoSubject subject in photo.Subjects)
foreach (PhotoSubject subject in photo.PhotoSubjects)
{
if (subjectUserIds.Contains(subject.UserId))
{

View file

@ -0,0 +1,55 @@
using LBPUnion.ProjectLighthouse.Database;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ProjectLighthouse.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20230215195324_ChangeLocationStorage")]
public partial class FixPhotoAndSubjectRelation : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "PhotoId",
table: "PhotoSubjects",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.Sql(
"UPDATE PhotoSubjects as ps inner join Photos as p on find_in_set(ps.PhotoSubjectId, p.PhotoSubjectCollection) SET ps.PhotoId = p.PhotoId");
migrationBuilder.CreateIndex(
name: "IX_PhotoSubjects_PhotoId",
table: "PhotoSubjects",
column: "PhotoId");
migrationBuilder.AddForeignKey(
name: "FK_PhotoSubjects_Photos_PhotoId",
table: "PhotoSubjects",
column: "PhotoId",
principalTable: "Photos",
principalColumn: "PhotoId",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_PhotoSubjects_Photos_PhotoId",
table: "PhotoSubjects");
migrationBuilder.DropIndex(
name: "IX_PhotoSubjects_PhotoId",
table: "PhotoSubjects");
migrationBuilder.DropColumn(
name: "PhotoId",
table: "PhotoSubjects");
}
}
}

View file

@ -1,6 +1,5 @@
// <auto-generated />
using System;
using LBPUnion.ProjectLighthouse;
using LBPUnion.ProjectLighthouse.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
@ -719,11 +718,16 @@ namespace ProjectLighthouse.Migrations
b.Property<string>("Bounds")
.HasColumnType("longtext");
b.Property<int>("PhotoId")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("PhotoSubjectId");
b.HasIndex("PhotoId");
b.HasIndex("UserId");
b.ToTable("PhotoSubjects");
@ -1296,12 +1300,20 @@ namespace ProjectLighthouse.Migrations
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Entities.Profile.PhotoSubject", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.Entities.Profile.Photo", "Photo")
.WithMany("PhotoSubjects")
.HasForeignKey("PhotoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LBPUnion.ProjectLighthouse.Types.Entities.Profile.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Photo");
b.Navigation("User");
});
@ -1348,6 +1360,11 @@ namespace ProjectLighthouse.Migrations
b.Navigation("User");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Entities.Profile.Photo", b =>
{
b.Navigation("PhotoSubjects");
});
#pragma warning restore 612, 618
}
}

View file

@ -1,5 +1,4 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@ -9,7 +8,6 @@ using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Types.Entities.Profile;
@ -65,31 +63,7 @@ public class Photo
[XmlElement("plan")]
public string PlanHash { get; set; } = "";
[NotMapped]
public List<PhotoSubject> Subjects {
get {
if (this.SubjectsXmlDontUseLiterallyEver != null) return this.SubjectsXmlDontUseLiterallyEver;
if (this._subjects != null) return this._subjects;
List<PhotoSubject> response = new();
using DatabaseContext database = new();
foreach (string idStr in this.PhotoSubjectIds.Where(idStr => !string.IsNullOrEmpty(idStr)))
{
if (!int.TryParse(idStr, out int id)) throw new InvalidCastException(idStr + " is not a valid number.");
PhotoSubject? photoSubject = database.PhotoSubjects
.Include(p => p.User)
.FirstOrDefault(p => p.PhotoSubjectId == id);
if (photoSubject == null) continue;
response.Add(photoSubject);
}
return response;
}
set => this._subjects = value;
}
public virtual ICollection<PhotoSubject> PhotoSubjects { get; set; } = new HashSet<PhotoSubject>();
[NotMapped]
[XmlIgnore]
@ -134,7 +108,7 @@ public class Photo
string slot = LbpSerializer.TaggedStringElement("slot", LbpSerializer.StringElement("id", slotId), "type", slotType.ToString().ToLower());
if (slotId == 0) slot = "";
string subjectsAggregate = this.Subjects.Aggregate(string.Empty, (s, subject) => s + subject.Serialize());
string subjectsAggregate = this.PhotoSubjects.Aggregate(string.Empty, (s, subject) => s + subject.Serialize());
string photo = LbpSerializer.StringElement("id", this.PhotoId) +
LbpSerializer.StringElement("small", this.SmallHash) +

View file

@ -20,10 +20,17 @@ public class PhotoSubject
public int UserId { get; set; }
[XmlIgnore]
[ForeignKey(nameof(UserId))]
[JsonIgnore]
[ForeignKey(nameof(UserId))]
public User User { get; set; }
public int PhotoId { get; set; }
[XmlIgnore]
[JsonIgnore]
[ForeignKey(nameof(PhotoId))]
public Photo Photo { get; set; }
[NotMapped]
[XmlElement("npHandle")]
public string Username { get; set; }

View file

@ -7,9 +7,11 @@ using System.Text.Json.Serialization;
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types.Misc;
using LBPUnion.ProjectLighthouse.Types.Users;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Types.Entities.Profile;
@ -224,6 +226,37 @@ public class User
public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1)
{
long start = TimeHelper.TimestampMillis;
var stats = this.database.Users.AsNoTracking().DefaultIfEmpty().Select(_ => new
{
ReviewCount = this.database.Reviews.Count(r => r.ReviewerId == this.UserId),
CommentCount = this.database.Comments.Count(c => c.PosterUserId == this.UserId),
PhotoCount = this.database.Photos.Count(p => p.CreatorId == this.UserId),
PlaylistCount = this.database.Playlists.Count(p => p.CreatorId == this.UserId),
HeartedLevelCount = this.database.HeartedLevels.Count(p => p.UserId == this.UserId),
HeartedUserCount = this.database.HeartedProfiles.Count(p => p.UserId == this.UserId),
HeartedPlaylistCount = this.database.HeartedPlaylists.Count(p => p.UserId == this.UserId),
QueuedLevelCount = this.database.QueuedLevels.Count(p => p.UserId == this.UserId),
})
.FirstOrDefault();
Console.WriteLine(@$"Fetching stats in single query took {TimeHelper.TimestampMillis - start}");
start = TimeHelper.TimestampMillis;
var stats2 = new
{
ReviewCount = this.Reviews,
CommentCount = this.Comments,
PhotoCount = this.PhotosByMe,
PlaylistCount = this.Lists,
HeartedLevelCount = this.HeartedLevels,
HeartedUserCount = this.HeartedUsers,
HeartedPlaylistCount = this.HeartedPlaylists,
QueuedLevelCount = this.QueuedLevels,
};
Console.WriteLine(@$"Fetching stats in multiple queries took {TimeHelper.TimestampMillis - start}");
start = TimeHelper.TimestampMillis;
int photosWithMe = this.PhotosWithMe();
Console.WriteLine(@$"Fetching photos with me took {TimeHelper.TimestampMillis - start}");
string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) +
LbpSerializer.StringElement("game", (int)gameVersion) +
this.serializeSlots(gameVersion) +