[backend] Refactor postgres jsonb columns

This commit is contained in:
Laura Hausmann 2024-02-05 18:26:08 +01:00
parent 843b7edb86
commit 29581d4fb2
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
17 changed files with 6033 additions and 38 deletions

View file

@ -37,7 +37,8 @@ public class UserRenderer(IOptions<Config.InstanceSection> config) {
HeaderStaticUrl = user.BannerUrl ?? _transparent, //TODO
MovedToAccount = null, //TODO
IsBot = user.IsBot,
IsDiscoverable = user.IsExplorable
IsDiscoverable = user.IsExplorable,
Fields = [] //TODO
};
return res;

View file

@ -22,9 +22,15 @@ public class Account : IEntity {
[J("moved")] public required Account? MovedToAccount { get; set; }
[J("bot")] public required bool IsBot { get; set; }
[J("discoverable")] public required bool IsDiscoverable { get; set; }
[J("fields")] public required Field[] Fields { get; set; }
[J("source")] public object? Source => null; //FIXME
[J("fields")] public object[] Fields => []; //FIXME
[J("emojis")] public object[] Emoji => []; //FIXME
[J("id")] public required string Id { get; set; }
}
public class Field {
[J("name")] public required string Name { get; set; }
[J("value")] public required string Value { get; set; }
[J("verified_at")] public string? VerifiedAt { get; set; }
}

View file

@ -2,5 +2,5 @@ namespace Iceshrimp.Backend.Core.Configuration;
public static class Constants {
public const string ActivityStreamsNs = "https://www.w3.org/ns/activitystreams";
public static readonly string[] SystemUsers = { "instance.actor", "relay.actor" };
public static readonly string[] SystemUsers = ["instance.actor", "relay.actor"];
}

View file

@ -100,6 +100,8 @@ public class DatabaseContext(DbContextOptions<DatabaseContext> options)
dataSourceBuilder.MapEnum<Relay.RelayStatus>();
dataSourceBuilder.MapEnum<UserProfile.UserProfileFFVisibility>();
dataSourceBuilder.EnableDynamicJson();
return dataSourceBuilder.Build();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Iceshrimp.Backend.Core.Database.Migrations
{
/// <inheritdoc />
public partial class RefactorJsonbColumns : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View file

@ -1,6 +1,7 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables;
using Microsoft.EntityFrameworkCore;
@ -303,7 +304,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnName("createdAt")
.HasComment("The created date of the Antenna.");
b.Property<string>("ExcludeKeywords")
b.Property<List<List<string>>>("ExcludeKeywords")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -315,14 +316,14 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnType("character varying(2048)")
.HasColumnName("expression");
b.Property<string>("Instances")
b.Property<List<string>>("Instances")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
.HasColumnName("instances")
.HasDefaultValueSql("'[]'::jsonb");
b.Property<string>("Keywords")
b.Property<List<List<string>>>("Keywords")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -860,7 +861,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnName("name")
.HasComment("The file name of the DriveFile.");
b.Property<string>("Properties")
b.Property<DriveFile.FileProperties>("Properties")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -868,7 +869,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasDefaultValueSql("'{}'::jsonb")
.HasComment("The any properties of the DriveFile. For example, it includes image width/height.");
b.Property<string>("RequestHeaders")
b.Property<Dictionary<string, string>>("RequestHeaders")
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
.HasColumnName("requestHeaders")
@ -1519,7 +1520,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnType("text")
.HasColumnName("bio");
b.Property<string>("Fields")
b.Property<Field[]>("Fields")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -1949,7 +1950,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnName("errorImageUrl")
.HasDefaultValueSql("'/static-assets/badges/error.png'::character varying");
b.Property<string>("ExperimentalFeatures")
b.Property<Dictionary<string, bool>>("ExperimentalFeatures")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -2399,7 +2400,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnType("character varying(256)")
.HasColumnName("name");
b.Property<string>("Reactions")
b.Property<Dictionary<string, long>>("Reactions")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -3624,7 +3625,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnName("createdAt")
.HasComment("The created date of the Signin.");
b.Property<string>("Headers")
b.Property<Dictionary<string, string>>("Headers")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("headers");
@ -4353,7 +4354,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnName("email")
.HasComment("The email address of the User.");
b.Property<string>("EmailNotificationTypes")
b.Property<List<string>>("EmailNotificationTypes")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -4383,7 +4384,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasDefaultValue(UserProfile.UserProfileFFVisibility.Public)
.HasColumnName("ffVisibility");
b.Property<string>("Fields")
b.Property<UserProfile.Field[]>("Fields")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -4414,7 +4415,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnName("location")
.HasComment("The location of the User.");
b.Property<string>("Mentions")
b.Property<List<UserProfile.MentionedRemoteUsers>>("Mentions")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -4429,7 +4430,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasColumnName("moderationNote")
.HasDefaultValueSql("''::character varying");
b.Property<string>("MutedInstances")
b.Property<List<string>>("MutedInstances")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")
@ -4437,7 +4438,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
.HasDefaultValueSql("'[]'::jsonb")
.HasComment("List of instances muted by the user.");
b.Property<string>("MutedWords")
b.Property<List<List<string>>>("MutedWords")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("jsonb")

View file

@ -40,7 +40,7 @@ public class Antenna {
public string? UserListId { get; set; }
[Column("keywords", TypeName = "jsonb")]
public string Keywords { get; set; } = null!;
public List<List<string>> Keywords { get; set; } = [];
[Column("withFile")] public bool WithFile { get; set; }
@ -62,10 +62,11 @@ public class Antenna {
public List<string> Users { get; set; } = null!;
[Column("excludeKeywords", TypeName = "jsonb")]
public string ExcludeKeywords { get; set; } = null!;
public List<List<string>> ExcludeKeywords { get; set; } = [];
//TODO: refactor this column (this should have been a varchar[])
[Column("instances", TypeName = "jsonb")]
public string Instances { get; set; } = null!;
public List<string> Instances { get; set; } = [];
[ForeignKey("UserId")]
[InverseProperty(nameof(Tables.User.Antennas))]

View file

@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
namespace Iceshrimp.Backend.Core.Database.Tables;
@ -82,7 +83,7 @@ public class DriveFile {
/// The any properties of the DriveFile. For example, it includes image width/height.
/// </summary>
[Column("properties", TypeName = "jsonb")]
public string Properties { get; set; } = null!;
public FileProperties Properties { get; set; } = null!;
[Column("storedInternal")] public bool StoredInternal { get; set; }
@ -159,13 +160,14 @@ public class DriveFile {
public string? WebpublicType { get; set; }
[Column("requestHeaders", TypeName = "jsonb")]
public string? RequestHeaders { get; set; }
public Dictionary<string, string>? RequestHeaders { get; set; }
[Column("requestIp")]
[StringLength(128)]
public string? RequestIp { get; set; }
[InverseProperty(nameof(Channel.Banner))] public virtual ICollection<Channel> Channels { get; set; } = new List<Channel>();
[InverseProperty(nameof(Channel.Banner))]
public virtual ICollection<Channel> Channels { get; set; } = new List<Channel>();
[ForeignKey("FolderId")]
[InverseProperty(nameof(DriveFolder.DriveFiles))]
@ -174,13 +176,23 @@ public class DriveFile {
[InverseProperty(nameof(MessagingMessage.File))]
public virtual ICollection<MessagingMessage> MessagingMessages { get; set; } = new List<MessagingMessage>();
[InverseProperty(nameof(Page.EyeCatchingImage))] public virtual ICollection<Page> Pages { get; set; } = new List<Page>();
[InverseProperty(nameof(Page.EyeCatchingImage))]
public virtual ICollection<Page> Pages { get; set; } = new List<Page>();
[ForeignKey("UserId")]
[InverseProperty(nameof(Tables.User.DriveFiles))]
public virtual User? User { get; set; }
[InverseProperty(nameof(Tables.User.Avatar))] public virtual User? UserAvatar { get; set; }
[InverseProperty(nameof(Tables.User.Avatar))]
public virtual User? UserAvatar { get; set; }
[InverseProperty(nameof(Tables.User.Banner))] public virtual User? UserBanner { get; set; }
[InverseProperty(nameof(Tables.User.Banner))]
public virtual User? UserBanner { get; set; }
public class FileProperties {
[J("width")] public int? Width { get; set; }
[J("height")] public int? Height { get; set; }
[J("orientation")] public int? Orientation { get; set; }
[J("avgColor")] public string? AverageColor { get; set; }
}
}

View file

@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
namespace Iceshrimp.Backend.Core.Database.Tables;
@ -14,7 +15,7 @@ public class HtmlUserCacheEntry {
[Column("bio")] public string? Bio { get; set; }
[Column("fields", TypeName = "jsonb")] public string Fields { get; set; } = null!;
[Column("fields", TypeName = "jsonb")] public Field[] Fields { get; set; } = null!;
[ForeignKey("UserId")]
[InverseProperty(nameof(Tables.User.HtmlUserCacheEntry))]

View file

@ -269,7 +269,7 @@ public class Meta {
public List<string> SilencedHosts { get; set; } = null!;
[Column("experimentalFeatures", TypeName = "jsonb")]
public string ExperimentalFeatures { get; set; } = null!;
public Dictionary<string, bool> ExperimentalFeatures { get; set; } = null!;
[Column("enableServerMachineStats")] public bool EnableServerMachineStats { get; set; }

View file

@ -22,6 +22,7 @@ public class ModerationLog {
[Column("type")] [StringLength(128)] public string Type { get; set; } = null!;
//TODO: refactor this column (it's currently a Dictionary<string, any>, which is terrible)
[Column("info", TypeName = "jsonb")] public string Info { get; set; } = null!;
[ForeignKey("UserId")]

View file

@ -81,7 +81,7 @@ public class Note : IEntity {
[Column("repliesCount")] public short RepliesCount { get; set; }
[Column("reactions", TypeName = "jsonb")]
public string Reactions { get; set; } = null!;
public Dictionary<string, long> Reactions { get; set; } = null!;
/// <summary>
/// The URI of a note. it will be null when the note is local.

View file

@ -55,9 +55,11 @@ public class Page {
[StringLength(32)]
public string? EyeCatchingImageId { get; set; }
//TODO: refactor this column (it's currently a Dictionary<string, any>, which is terrible)
[Column("content", TypeName = "jsonb")]
public string Content { get; set; } = null!;
//TODO: refactor this column (it's currently a Dictionary<string, any>, which is terrible)
[Column("variables", TypeName = "jsonb")]
public string Variables { get; set; } = null!;

View file

@ -48,6 +48,7 @@ public class RegistryItem {
/// <summary>
/// The value of the RegistryItem.
/// </summary>
//TODO: refactor this column (it's currently a Nullable<any>, which is terrible)
[Column("value", TypeName = "jsonb")]
public string? Value { get; set; }

View file

@ -23,7 +23,7 @@ public class Signin {
[Column("ip")] [StringLength(128)] public string Ip { get; set; } = null!;
[Column("headers", TypeName = "jsonb")]
public string Headers { get; set; } = null!;
public Dictionary<string, string> Headers { get; set; } = null!;
[Column("success")] public bool Success { get; set; }

View file

@ -2,6 +2,7 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using NpgsqlTypes;
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
namespace Iceshrimp.Backend.Core.Database.Tables;
@ -36,7 +37,7 @@ public class UserProfile {
[StringLength(2048)]
public string? Description { get; set; }
[Column("fields", TypeName = "jsonb")] public string Fields { get; set; } = null!;
[Column("fields", TypeName = "jsonb")] public Field[] Fields { get; set; } = null!;
/// <summary>
/// Remote URL of the user.
@ -45,8 +46,7 @@ public class UserProfile {
[StringLength(512)]
public string? Url { get; set; }
[Column("ffVisibility")]
public UserProfileFFVisibility FFVisibility { get; set; }
[Column("ffVisibility")] public UserProfileFFVisibility FFVisibility { get; set; }
[Column("mutingNotificationTypes")]
public List<Notification.NotificationType> MutingNotificationTypes { get; set; } = [];
@ -84,6 +84,7 @@ public class UserProfile {
/// <summary>
/// The client-specific data of the User.
/// </summary>
//TODO: refactor this column (it's currently a Dictionary<string, any>, which is terrible)
[Column("clientData", TypeName = "jsonb")]
public string ClientData { get; set; } = null!;
@ -111,9 +112,11 @@ public class UserProfile {
/// <summary>
/// The room data of the User.
/// </summary>
//TODO: refactor this column (it's currently a Dictionary<string, any>, which is terrible)
[Column("room", TypeName = "jsonb")]
public string Room { get; set; } = null!;
//TODO: refactor this column (it's currently a Dictionary<string, any>, which is terrible)
[Column("integrations", TypeName = "jsonb")]
public string Integrations { get; set; } = null!;
@ -122,7 +125,7 @@ public class UserProfile {
[Column("enableWordMute")] public bool EnableWordMute { get; set; }
[Column("mutedWords", TypeName = "jsonb")]
public string MutedWords { get; set; } = null!;
public List<List<string>> MutedWords { get; set; } = null!;
/// <summary>
/// Whether reject index by crawler.
@ -132,16 +135,18 @@ public class UserProfile {
[Column("receiveAnnouncementEmail")] public bool ReceiveAnnouncementEmail { get; set; }
//TODO: refactor this column (this should have been NotificationTypeEnum[])
[Column("emailNotificationTypes", TypeName = "jsonb")]
public string EmailNotificationTypes { get; set; } = null!;
public List<string> EmailNotificationTypes { get; set; } = null!;
[Column("lang")] [StringLength(32)] public string? Lang { get; set; }
/// <summary>
/// List of instances muted by the user.
/// </summary>
//TODO: refactor this column (this should have been a varchar[])
[Column("mutedInstances", TypeName = "jsonb")]
public string MutedInstances { get; set; } = null!;
public List<string> MutedInstances { get; set; } = null!;
[Column("publicReactions")] public bool PublicReactions { get; set; }
@ -152,7 +157,7 @@ public class UserProfile {
[Column("preventAiLearning")] public bool PreventAiLearning { get; set; }
[Column("mentions", TypeName = "jsonb")]
public string Mentions { get; set; } = null!;
public List<MentionedRemoteUsers> Mentions { get; set; } = null!;
[ForeignKey("PinnedPageId")]
[InverseProperty(nameof(Page.UserProfile))]
@ -168,4 +173,17 @@ public class UserProfile {
[PgName("followers")] Followers,
[PgName("private")] Private,
}
public class Field {
[J("name")] public required string Name { get; set; }
[J("value")] public required string Value { get; set; }
[J("verified")] public bool? IsVerified { get; set; }
}
public class MentionedRemoteUsers {
[J("uri")] public required string Uri { get; set; }
[J("url")] public string? Url { get; set; }
[J("username")] public required string Username { get; set; }
[J("host")] public required string Host { get; set; }
}
}