[backend/core] Denormalize like counts (ISH-110)
This commit is contained in:
parent
fe06c64242
commit
e42812d2b0
9 changed files with 6228 additions and 21 deletions
|
@ -33,8 +33,6 @@ public class NoteRenderer(
|
||||||
text += $"\n\nRE: {quoteUri}"; //TODO: render as inline quote
|
text += $"\n\nRE: {quoteUri}"; //TODO: render as inline quote
|
||||||
}
|
}
|
||||||
|
|
||||||
var likeCount = data?.LikeCounts?.GetValueOrDefault(note.Id, 0) ??
|
|
||||||
await db.NoteLikes.CountAsync(p => p.Note == note);
|
|
||||||
var liked = data?.LikedNotes?.Contains(note.Id) ??
|
var liked = data?.LikedNotes?.Contains(note.Id) ??
|
||||||
await db.NoteLikes.AnyAsync(p => p.Note == note && p.User == user);
|
await db.NoteLikes.AnyAsync(p => p.Note == note && p.User == user);
|
||||||
var renoted = data?.Renotes?.Contains(note.Id) ??
|
var renoted = data?.Renotes?.Contains(note.Id) ??
|
||||||
|
@ -82,7 +80,7 @@ public class NoteRenderer(
|
||||||
EditedAt = note.UpdatedAt?.ToStringIso8601Like(),
|
EditedAt = note.UpdatedAt?.ToStringIso8601Like(),
|
||||||
RepliesCount = note.RepliesCount,
|
RepliesCount = note.RepliesCount,
|
||||||
RenoteCount = note.RenoteCount,
|
RenoteCount = note.RenoteCount,
|
||||||
FavoriteCount = likeCount,
|
FavoriteCount = note.LikeCount,
|
||||||
IsFavorited = liked,
|
IsFavorited = liked,
|
||||||
IsRenoted = renoted,
|
IsRenoted = renoted,
|
||||||
IsBookmarked = false, //FIXME
|
IsBookmarked = false, //FIXME
|
||||||
|
@ -133,14 +131,6 @@ public class NoteRenderer(
|
||||||
return (await userRenderer.RenderManyAsync(users.DistinctBy(p => p.Id))).ToList();
|
return (await userRenderer.RenderManyAsync(users.DistinctBy(p => p.Id))).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, int>> GetLikeCounts(IEnumerable<Note> notes)
|
|
||||||
{
|
|
||||||
return await db.NoteLikes.Where(p => notes.Contains(p.Note))
|
|
||||||
.Select(p => p.NoteId)
|
|
||||||
.GroupBy(p => p)
|
|
||||||
.ToDictionaryAsync(p => p.First(), p => p.Count());
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<List<string>> GetLikedNotes(IEnumerable<Note> notes, User? user)
|
private async Task<List<string>> GetLikedNotes(IEnumerable<Note> notes, User? user)
|
||||||
{
|
{
|
||||||
if (user == null) return [];
|
if (user == null) return [];
|
||||||
|
@ -193,7 +183,6 @@ public class NoteRenderer(
|
||||||
Accounts = accounts ?? await GetAccounts(noteList.Select(p => p.User)),
|
Accounts = accounts ?? await GetAccounts(noteList.Select(p => p.User)),
|
||||||
Mentions = await GetMentions(noteList),
|
Mentions = await GetMentions(noteList),
|
||||||
Attachments = await GetAttachments(noteList),
|
Attachments = await GetAttachments(noteList),
|
||||||
LikeCounts = await GetLikeCounts(noteList),
|
|
||||||
LikedNotes = await GetLikedNotes(noteList, user),
|
LikedNotes = await GetLikedNotes(noteList, user),
|
||||||
Renotes = await GetRenotes(noteList, user),
|
Renotes = await GetRenotes(noteList, user),
|
||||||
Emoji = await GetEmoji(noteList)
|
Emoji = await GetEmoji(noteList)
|
||||||
|
@ -204,13 +193,12 @@ public class NoteRenderer(
|
||||||
|
|
||||||
public class NoteRendererDto
|
public class NoteRendererDto
|
||||||
{
|
{
|
||||||
public List<AccountEntity>? Accounts;
|
public List<AccountEntity>? Accounts;
|
||||||
public List<MentionEntity>? Mentions;
|
public List<MentionEntity>? Mentions;
|
||||||
public List<AttachmentEntity>? Attachments;
|
public List<AttachmentEntity>? Attachments;
|
||||||
public Dictionary<string, int>? LikeCounts;
|
public List<string>? LikedNotes;
|
||||||
public List<string>? LikedNotes;
|
public List<string>? Renotes;
|
||||||
public List<string>? Renotes;
|
public List<EmojiEntity>? Emoji;
|
||||||
public List<EmojiEntity>? Emoji;
|
public bool Source;
|
||||||
public bool Source;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -96,6 +96,7 @@ public class StatusController(
|
||||||
throw GracefulException.RecordNotFound();
|
throw GracefulException.RecordNotFound();
|
||||||
|
|
||||||
await noteSvc.LikeNoteAsync(note, user);
|
await noteSvc.LikeNoteAsync(note, user);
|
||||||
|
note.LikeCount++; // we do not want to call save changes after this point
|
||||||
return await GetNote(id);
|
return await GetNote(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +114,7 @@ public class StatusController(
|
||||||
throw GracefulException.RecordNotFound();
|
throw GracefulException.RecordNotFound();
|
||||||
|
|
||||||
await noteSvc.UnlikeNoteAsync(note, user);
|
await noteSvc.UnlikeNoteAsync(note, user);
|
||||||
|
note.LikeCount--; // we do not want to call save changes after this point
|
||||||
return await GetNote(id);
|
return await GetNote(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -609,6 +609,7 @@ public class DatabaseContext(DbContextOptions<DatabaseContext> options)
|
||||||
entity.Property(e => e.Emojis).HasDefaultValueSql("'{}'::character varying[]");
|
entity.Property(e => e.Emojis).HasDefaultValueSql("'{}'::character varying[]");
|
||||||
entity.Property(e => e.FileIds).HasDefaultValueSql("'{}'::character varying[]");
|
entity.Property(e => e.FileIds).HasDefaultValueSql("'{}'::character varying[]");
|
||||||
entity.Property(e => e.HasPoll).HasDefaultValue(false);
|
entity.Property(e => e.HasPoll).HasDefaultValue(false);
|
||||||
|
entity.Property(e => e.LikeCount).HasDefaultValue(0);
|
||||||
entity.Property(e => e.LocalOnly).HasDefaultValue(false);
|
entity.Property(e => e.LocalOnly).HasDefaultValue(false);
|
||||||
entity.Property(e => e.MentionedRemoteUsers).HasDefaultValueSql("'[]'::jsonb");
|
entity.Property(e => e.MentionedRemoteUsers).HasDefaultValueSql("'[]'::jsonb");
|
||||||
entity.Property(e => e.Mentions).HasDefaultValueSql("'{}'::character varying[]");
|
entity.Property(e => e.Mentions).HasDefaultValueSql("'{}'::character varying[]");
|
||||||
|
|
6170
Iceshrimp.Backend/Core/Database/Migrations/20240304013243_AddNoteLikeCountColumn.Designer.cs
generated
Normal file
6170
Iceshrimp.Backend/Core/Database/Migrations/20240304013243_AddNoteLikeCountColumn.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,29 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Iceshrimp.Backend.Core.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddNoteLikeCountColumn : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "likeCount",
|
||||||
|
table: "note",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "likeCount",
|
||||||
|
table: "note");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2457,6 +2457,12 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
|
||||||
.HasDefaultValue(false)
|
.HasDefaultValue(false)
|
||||||
.HasColumnName("hasPoll");
|
.HasColumnName("hasPoll");
|
||||||
|
|
||||||
|
b.Property<int>("LikeCount")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasDefaultValue(0)
|
||||||
|
.HasColumnName("likeCount");
|
||||||
|
|
||||||
b.Property<bool>("LocalOnly")
|
b.Property<bool>("LocalOnly")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
|
|
|
@ -86,6 +86,8 @@ public class Note : IEntity
|
||||||
|
|
||||||
[Column("repliesCount")] public short RepliesCount { get; set; }
|
[Column("repliesCount")] public short RepliesCount { get; set; }
|
||||||
|
|
||||||
|
[Column("likeCount")] public int LikeCount { get; set; }
|
||||||
|
|
||||||
[Column("reactions", TypeName = "jsonb")]
|
[Column("reactions", TypeName = "jsonb")]
|
||||||
public Dictionary<string, long> Reactions { get; set; } = null!;
|
public Dictionary<string, long> Reactions { get; set; } = null!;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,9 @@ public class DatabaseMaintenanceService(DatabaseContext db)
|
||||||
await db.Notes.ExecuteUpdateAsync(p => p.SetProperty(n => n.RenoteCount,
|
await db.Notes.ExecuteUpdateAsync(p => p.SetProperty(n => n.RenoteCount,
|
||||||
n => db.Notes.Count(r => r.IsPureRenote && r.Renote == n))
|
n => db.Notes.Count(r => r.IsPureRenote && r.Renote == n))
|
||||||
.SetProperty(n => n.RepliesCount,
|
.SetProperty(n => n.RepliesCount,
|
||||||
n => db.Notes.Count(r => r.Reply == n)));
|
n => db.Notes.Count(r => r.Reply == n))
|
||||||
|
.SetProperty(n => n.LikeCount,
|
||||||
|
n => db.NoteLikes.Count(r => r.Note == n)));
|
||||||
//TODO: update reaction counts as well? (can likely not be done database-side :/)
|
//TODO: update reaction counts as well? (can likely not be done database-side :/)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -727,6 +727,9 @@ public class NoteService(
|
||||||
|
|
||||||
await db.NoteLikes.AddAsync(like);
|
await db.NoteLikes.AddAsync(like);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
await db.Notes.Where(p => p.Id == note.Id)
|
||||||
|
.ExecuteUpdateAsync(p => p.SetProperty(n => n.LikeCount, n => n.LikeCount + 1));
|
||||||
|
|
||||||
if (user.Host == null && note.UserHost != null)
|
if (user.Host == null && note.UserHost != null)
|
||||||
{
|
{
|
||||||
var activity = activityRenderer.RenderLike(note, user);
|
var activity = activityRenderer.RenderLike(note, user);
|
||||||
|
@ -742,6 +745,10 @@ public class NoteService(
|
||||||
{
|
{
|
||||||
var count = await db.NoteLikes.Where(p => p.Note == note && p.User == actor).ExecuteDeleteAsync();
|
var count = await db.NoteLikes.Where(p => p.Note == note && p.User == actor).ExecuteDeleteAsync();
|
||||||
if (count == 0) return;
|
if (count == 0) return;
|
||||||
|
|
||||||
|
await db.Notes.Where(p => p.Id == note.Id)
|
||||||
|
.ExecuteUpdateAsync(p => p.SetProperty(n => n.LikeCount, n => n.LikeCount - count));
|
||||||
|
|
||||||
if (actor.Host == null && note.UserHost != null)
|
if (actor.Host == null && note.UserHost != null)
|
||||||
{
|
{
|
||||||
var activity = activityRenderer.RenderUndo(userRenderer.RenderLite(actor),
|
var activity = activityRenderer.RenderUndo(userRenderer.RenderLite(actor),
|
||||||
|
|
Loading…
Add table
Reference in a new issue