perf(backend): avoid N+1 selects from user
table when packing many entities (#13911)
* perf(backend): avoid N+1 selects from `user` table when packing many entities * perf(backend): use `packMany` instead of mapping to `pack`
This commit is contained in:
@@ -290,6 +290,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||
_hint_?: {
|
||||
myReactions: Map<MiNote['id'], string | null>;
|
||||
packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
|
||||
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>
|
||||
};
|
||||
},
|
||||
): Promise<Packed<'Note'>> {
|
||||
@@ -319,12 +320,13 @@ export class NoteEntityService implements OnModuleInit {
|
||||
.filter(x => x.startsWith(':') && x.includes('@') && !x.includes('@.')) // リモートカスタム絵文字のみ
|
||||
.map(x => this.reactionService.decodeReaction(x).reaction.replaceAll(':', ''));
|
||||
const packedFiles = options?._hint_?.packedFiles;
|
||||
const packedUsers = options?._hint_?.packedUsers;
|
||||
|
||||
const packed: Packed<'Note'> = await awaitAll({
|
||||
id: note.id,
|
||||
createdAt: this.idService.parse(note.id).date.toISOString(),
|
||||
userId: note.userId,
|
||||
user: this.userEntityService.pack(note.user ?? note.userId, me),
|
||||
user: packedUsers?.get(note.userId) ?? this.userEntityService.pack(note.user ?? note.userId, me),
|
||||
text: text,
|
||||
cw: note.cw,
|
||||
visibility: note.visibility,
|
||||
@@ -449,12 +451,20 @@ export class NoteEntityService implements OnModuleInit {
|
||||
// TODO: 本当は renote とか reply がないのに renoteId とか replyId があったらここで解決しておく
|
||||
const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(isNotNull);
|
||||
const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds) : new Map();
|
||||
const users = [
|
||||
...notes.map(({ user, userId }) => user ?? userId),
|
||||
...notes.map(({ replyUserId }) => replyUserId).filter(isNotNull),
|
||||
...notes.map(({ renoteUserId }) => renoteUserId).filter(isNotNull),
|
||||
];
|
||||
const packedUsers = await this.userEntityService.packMany(users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
|
||||
return await Promise.all(notes.map(n => this.pack(n, me, {
|
||||
...options,
|
||||
_hint_: {
|
||||
myReactions: myReactionsMap,
|
||||
packedFiles,
|
||||
packedUsers,
|
||||
},
|
||||
})));
|
||||
}
|
||||
|
Reference in New Issue
Block a user