perf(server): Reduce database query
This commit is contained in:
		
							parent
							
								
									1f0abef084
								
							
						
					
					
						commit
						8f41dfec2e
					
				
					 3 changed files with 53 additions and 1 deletions
				
			
		| 
						 | 
					@ -1,7 +1,10 @@
 | 
				
			||||||
 | 
					import { In } from 'typeorm';
 | 
				
			||||||
import { Emojis } from '../models';
 | 
					import { Emojis } from '../models';
 | 
				
			||||||
import { Emoji } from '../models/entities/emoji';
 | 
					import { Emoji } from '../models/entities/emoji';
 | 
				
			||||||
 | 
					import { Note } from '../models/entities/note';
 | 
				
			||||||
import { Cache } from './cache';
 | 
					import { Cache } from './cache';
 | 
				
			||||||
import { isSelfHost, toPunyNullable } from './convert-host';
 | 
					import { isSelfHost, toPunyNullable } from './convert-host';
 | 
				
			||||||
 | 
					import { decodeReaction } from './reaction-lib';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cache = new Cache<Emoji | null>(1000 * 60 * 60);
 | 
					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);
 | 
						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 { toString } from '../../mfm/to-string';
 | 
				
			||||||
import { parse } from '../../mfm/parse';
 | 
					import { parse } from '../../mfm/parse';
 | 
				
			||||||
import { NoteReaction } from '../entities/note-reaction';
 | 
					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>;
 | 
					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, {
 | 
							return await Promise.all(notes.map(n => this.pack(n, me, {
 | 
				
			||||||
			...options,
 | 
								...options,
 | 
				
			||||||
			_hint_: {
 | 
								_hint_: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import { SchemaType } from '../../misc/schema';
 | 
				
			||||||
import { Note } from '../entities/note';
 | 
					import { Note } from '../entities/note';
 | 
				
			||||||
import { NoteReaction } from '../entities/note-reaction';
 | 
					import { NoteReaction } from '../entities/note-reaction';
 | 
				
			||||||
import { User } from '../entities/user';
 | 
					import { User } from '../entities/user';
 | 
				
			||||||
 | 
					import { aggregateNoteEmojis, prefetchEmojis } from '../../misc/populate-emojis';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type PackedNotification = SchemaType<typeof packedNotificationSchema>;
 | 
					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);
 | 
								myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							await prefetchEmojis(aggregateNoteEmojis(notes));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await Promise.all(notifications.map(x => this.pack(x, {
 | 
							return await Promise.all(notifications.map(x => this.pack(x, {
 | 
				
			||||||
			_hintForEachNotes_: {
 | 
								_hintForEachNotes_: {
 | 
				
			||||||
				myReactions: myReactionsMap
 | 
									myReactions: myReactionsMap
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue