perf(server): reduce db query
This commit is contained in:
parent
81ee9025fb
commit
ff19640171
3 changed files with 29 additions and 12 deletions
|
@ -840,6 +840,7 @@ tenMinutes: "10分"
|
||||||
oneHour: "1時間"
|
oneHour: "1時間"
|
||||||
oneDay: "1日"
|
oneDay: "1日"
|
||||||
oneWeek: "1週間"
|
oneWeek: "1週間"
|
||||||
|
reflectMayTakeTime: "反映されるまで時間がかかる場合があります。"
|
||||||
|
|
||||||
_emailUnavailable:
|
_emailUnavailable:
|
||||||
used: "既に使用されています"
|
used: "既に使用されています"
|
||||||
|
|
|
@ -35,6 +35,12 @@ import { Channel } from '@/models/entities/channel.js';
|
||||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
||||||
import { getAntennas } from '@/misc/antenna-cache.js';
|
import { getAntennas } from '@/misc/antenna-cache.js';
|
||||||
import { endedPollNotificationQueue } from '@/queue/queues.js';
|
import { endedPollNotificationQueue } from '@/queue/queues.js';
|
||||||
|
import { Cache } from '@/misc/cache.js';
|
||||||
|
import { UserProfile } from '@/models/entities/user-profile.js';
|
||||||
|
|
||||||
|
const usersCache = new Cache<MinimumUser>(Infinity);
|
||||||
|
|
||||||
|
const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5);
|
||||||
|
|
||||||
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
||||||
|
|
||||||
|
@ -91,6 +97,13 @@ class NotificationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MinimumUser = {
|
||||||
|
id: User['id'];
|
||||||
|
host: User['host'];
|
||||||
|
username: User['username'];
|
||||||
|
uri: User['uri'];
|
||||||
|
};
|
||||||
|
|
||||||
type Option = {
|
type Option = {
|
||||||
createdAt?: Date | null;
|
createdAt?: Date | null;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
|
@ -102,9 +115,9 @@ type Option = {
|
||||||
localOnly?: boolean | null;
|
localOnly?: boolean | null;
|
||||||
cw?: string | null;
|
cw?: string | null;
|
||||||
visibility?: string;
|
visibility?: string;
|
||||||
visibleUsers?: User[] | null;
|
visibleUsers?: MinimumUser[] | null;
|
||||||
channel?: Channel | null;
|
channel?: Channel | null;
|
||||||
apMentions?: User[] | null;
|
apMentions?: MinimumUser[] | null;
|
||||||
apHashtags?: string[] | null;
|
apHashtags?: string[] | null;
|
||||||
apEmojis?: string[] | null;
|
apEmojis?: string[] | null;
|
||||||
uri?: string | null;
|
uri?: string | null;
|
||||||
|
@ -199,7 +212,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
tags = tags.filter(tag => Array.from(tag || '').length <= 128).splice(0, 32);
|
tags = tags.filter(tag => Array.from(tag || '').length <= 128).splice(0, 32);
|
||||||
|
|
||||||
if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
|
if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
|
||||||
mentionedUsers.push(await Users.findOneOrFail(data.reply.userId));
|
mentionedUsers.push(await usersCache.fetch(data.reply.userId, () => Users.findOneOrFail(data.reply!.userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.visibility === 'specified') {
|
if (data.visibility === 'specified') {
|
||||||
|
@ -212,7 +225,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.reply && !data.visibleUsers.some(x => x.id === data.reply!.userId)) {
|
if (data.reply && !data.visibleUsers.some(x => x.id === data.reply!.userId)) {
|
||||||
data.visibleUsers.push(await Users.findOneOrFail(data.reply.userId));
|
data.visibleUsers.push(await usersCache.fetch(data.reply.userId, () => Users.findOneOrFail(data.reply!.userId)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,10 +254,12 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
incNotesCountOfUser(user);
|
incNotesCountOfUser(user);
|
||||||
|
|
||||||
// Word mute
|
// Word mute
|
||||||
// TODO: cache
|
mutedWordsCache.fetch(null, () => UserProfiles.find({
|
||||||
UserProfiles.find({
|
where: {
|
||||||
enableWordMute: true,
|
enableWordMute: true,
|
||||||
}).then(us => {
|
},
|
||||||
|
select: ['userId', 'mutedWords'],
|
||||||
|
})).then(us => {
|
||||||
for (const u of us) {
|
for (const u of us) {
|
||||||
checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => {
|
checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => {
|
||||||
if (shouldMute) {
|
if (shouldMute) {
|
||||||
|
@ -260,11 +275,12 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
});
|
});
|
||||||
|
|
||||||
// Antenna
|
// Antenna
|
||||||
|
// TODO: キャッシュしたい
|
||||||
Followings.createQueryBuilder('following')
|
Followings.createQueryBuilder('following')
|
||||||
.andWhere(`following.followeeId = :userId`, { userId: note.userId })
|
.andWhere(`following.followeeId = :userId`, { userId: note.userId })
|
||||||
.getMany()
|
.getMany()
|
||||||
.then(async followings => {
|
.then(async followings => {
|
||||||
const blockings = await Blockings.find({ blockerId: user.id }); // TODO: キャッシュしたい
|
const blockings = await Blockings.find({ blockerId: user.id });
|
||||||
const followers = followings.map(f => f.followerId);
|
const followers = followings.map(f => f.followerId);
|
||||||
for (const antenna of (await getAntennas())) {
|
for (const antenna of (await getAntennas())) {
|
||||||
if (blockings.some(blocking => blocking.blockeeId === antenna.userId)) continue; // この処理は checkHitAntenna 内でやるようにしてもいいかも
|
if (blockings.some(blocking => blocking.blockeeId === antenna.userId)) continue; // この処理は checkHitAntenna 内でやるようにしてもいいかも
|
||||||
|
@ -465,7 +481,7 @@ function incRenoteCount(renote: Note) {
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: User[]) {
|
async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) {
|
||||||
const insert = new Note({
|
const insert = new Note({
|
||||||
id: genId(data.createdAt!),
|
id: genId(data.createdAt!),
|
||||||
createdAt: data.createdAt!,
|
createdAt: data.createdAt!,
|
||||||
|
@ -597,7 +613,7 @@ async function notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createMentionedEvents(mentionedUsers: User[], note: Note, nm: NotificationManager) {
|
async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) {
|
||||||
for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) {
|
for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) {
|
||||||
const threadMuted = await NoteThreadMutings.findOne({
|
const threadMuted = await NoteThreadMutings.findOne({
|
||||||
userId: u.id,
|
userId: u.id,
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
</FormTextarea>
|
</FormTextarea>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="tab === 'hard'">
|
<div v-show="tab === 'hard'">
|
||||||
<MkInfo class="_formBlock">{{ $ts._wordMute.hardDescription }}</MkInfo>
|
<MkInfo class="_formBlock">{{ $ts._wordMute.hardDescription }} {{ $ts.reflectMayTakeTime }}</MkInfo>
|
||||||
<FormTextarea v-model="hardMutedWords" class="_formBlock">
|
<FormTextarea v-model="hardMutedWords" class="_formBlock">
|
||||||
<span>{{ $ts._wordMute.muteWords }}</span>
|
<span>{{ $ts._wordMute.muteWords }}</span>
|
||||||
<template #caption>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
|
<template #caption>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
|
||||||
|
|
Loading…
Reference in a new issue