perf(server): Reduce database query
This commit is contained in:
		
							parent
							
								
									1f0abef084
								
							
						
					
					
						commit
						8f41dfec2e
					
				|  | @ -1,7 +1,10 @@ | |||
| import { In } from 'typeorm'; | ||||
| import { Emojis } from '../models'; | ||||
| import { Emoji } from '../models/entities/emoji'; | ||||
| import { Note } from '../models/entities/note'; | ||||
| import { Cache } from './cache'; | ||||
| import { isSelfHost, toPunyNullable } from './convert-host'; | ||||
| import { decodeReaction } from './reaction-lib'; | ||||
| 
 | ||||
| const cache = new Cache<Emoji | null>(1000 * 60 * 60); | ||||
| 
 | ||||
|  | @ -70,3 +73,47 @@ export async function populateEmojis(emojiNames: string[], noteUserHost: string | |||
| 	return emojis.filter((x): x is PopulatedEmoji => x != null); | ||||
| } | ||||
| 
 | ||||
| export function aggregateNoteEmojis(notes: Note[]) { | ||||
| 	let emojis: { name: string | null; host: string | null; }[] = []; | ||||
| 	for (const note of notes) { | ||||
| 		emojis = emojis.concat(note.emojis | ||||
| 			.map(e => parseEmojiStr(e, note.userHost))); | ||||
| 		if (note.renote) { | ||||
| 			emojis = emojis.concat(note.renote.emojis | ||||
| 				.map(e => parseEmojiStr(e, note.renote!.userHost))); | ||||
| 			if (note.renote.user) { | ||||
| 				emojis = emojis.concat(note.renote.user.emojis | ||||
| 					.map(e => parseEmojiStr(e, note.renote!.userHost))); | ||||
| 			} | ||||
| 		} | ||||
| 		const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name != null) as typeof emojis; | ||||
| 		emojis = emojis.concat(customReactions); | ||||
| 		if (note.user) { | ||||
| 			emojis = emojis.concat(note.user.emojis | ||||
| 				.map(e => parseEmojiStr(e, note.userHost))); | ||||
| 		} | ||||
| 	} | ||||
| 	return emojis.filter(x => x.name != null) as { name: string; host: string | null; }[]; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します | ||||
|  */ | ||||
| export async function prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> { | ||||
| 	const notCachedEmojis = emojis.filter(emoji => cache.get(`${emoji.name} ${emoji.host}`) == null); | ||||
| 	const emojisQuery: any[] = []; | ||||
| 	const hosts = new Set(notCachedEmojis.map(e => e.host)); | ||||
| 	for (const host of hosts) { | ||||
| 		emojisQuery.push({ | ||||
| 			name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)), | ||||
| 			host: host | ||||
| 		}); | ||||
| 	} | ||||
| 	const _emojis = emojisQuery.length > 0 ? await Emojis.find({ | ||||
| 		where: emojisQuery, | ||||
| 		select: ['name', 'host', 'url'] | ||||
| 	}) : []; | ||||
| 	for (const emoji of _emojis) { | ||||
| 		cache.set(`${emoji.name} ${emoji.host}`, emoji); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '. | |||
| import { toString } from '../../mfm/to-string'; | ||||
| import { parse } from '../../mfm/parse'; | ||||
| import { NoteReaction } from '../entities/note-reaction'; | ||||
| import { populateEmojis } from '../../misc/populate-emojis'; | ||||
| import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '../../misc/populate-emojis'; | ||||
| 
 | ||||
| export type PackedNote = SchemaType<typeof packedNoteSchema>; | ||||
| 
 | ||||
|  | @ -259,6 +259,8 @@ export class NoteRepository extends Repository<Note> { | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		await prefetchEmojis(aggregateNoteEmojis(notes)); | ||||
| 
 | ||||
| 		return await Promise.all(notes.map(n => this.pack(n, me, { | ||||
| 			...options, | ||||
| 			_hint_: { | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import { SchemaType } from '../../misc/schema'; | |||
| import { Note } from '../entities/note'; | ||||
| import { NoteReaction } from '../entities/note-reaction'; | ||||
| import { User } from '../entities/user'; | ||||
| import { aggregateNoteEmojis, prefetchEmojis } from '../../misc/populate-emojis'; | ||||
| 
 | ||||
| export type PackedNotification = SchemaType<typeof packedNotificationSchema>; | ||||
| 
 | ||||
|  | @ -98,6 +99,8 @@ export class NotificationRepository extends Repository<Notification> { | |||
| 			myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); | ||||
| 		} | ||||
| 
 | ||||
| 		await prefetchEmojis(aggregateNoteEmojis(notes)); | ||||
| 
 | ||||
| 		return await Promise.all(notifications.map(x => this.pack(x, { | ||||
| 			_hintForEachNotes_: { | ||||
| 				myReactions: myReactionsMap | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue