From e523e54881834a50b7b5e1f0efebcc98cfc34dd7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 20 Mar 2021 11:05:33 +0900 Subject: [PATCH] perf(server): Reduce database query --- src/models/repositories/notification.ts | 67 ++++++++++++++++++--- src/server/api/endpoints/i/notifications.ts | 2 +- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index d593dc22a9..a9fe5e7b4e 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -1,8 +1,11 @@ -import { EntityRepository, Repository } from 'typeorm'; -import { Users, Notes, UserGroupInvitations, AccessTokens } from '..'; +import { EntityRepository, In, Repository } from 'typeorm'; +import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '..'; import { Notification } from '../entities/notification'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; +import { Note } from '../entities/note'; +import { NoteReaction } from '../entities/note-reaction'; +import { User } from '../entities/user'; export type PackedNotification = SchemaType; @@ -10,6 +13,11 @@ export type PackedNotification = SchemaType; export class NotificationRepository extends Repository { public async pack( src: Notification['id'] | Notification, + options: { + _hintForEachNotes_: { + myReactions: Map; + }; + } ): Promise { const notification = typeof src === 'object' ? src : await this.findOneOrFail(src); const token = notification.appAccessTokenId ? await AccessTokens.findOneOrFail(notification.appAccessTokenId) : null; @@ -22,23 +30,41 @@ export class NotificationRepository extends Repository { userId: notification.notifierId, user: notification.notifierId ? Users.pack(notification.notifier || notification.notifierId) : null, ...(notification.type === 'mention' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'reply' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'renote' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'quote' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'reaction' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), reaction: notification.reaction } : {}), ...(notification.type === 'pollVote' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), choice: notification.choice } : {}), ...(notification.type === 'groupInvited' ? { @@ -52,10 +78,31 @@ export class NotificationRepository extends Repository { }); } - public packMany( + public async packMany( notifications: Notification[], + meId: User['id'] ) { - return Promise.all(notifications.map(x => this.pack(x))); + if (notifications.length === 0) return []; + + const notes = notifications.filter(x => x.note != null).map(x => x.note!); + const noteIds = notes.map(n => n.id); + const myReactionsMap = new Map(); + const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); + const targets = [...noteIds, ...renoteIds]; + const myReactions = await NoteReactions.find({ + userId: meId, + noteId: In(targets), + }); + + for (const target of targets) { + myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); + } + + return await Promise.all(notifications.map(x => this.pack(x, { + _hintForEachNotes_: { + myReactions: myReactionsMap + } + }))); } } diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 7a423edb8d..af49549d37 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -112,5 +112,5 @@ export default define(meta, async (ps, user) => { readNotification(user.id, notifications.map(x => x.id)); } - return await Notifications.packMany(notifications); + return await Notifications.packMany(notifications, user.id); });