forked from FoundKeyGang/FoundKey
perf: Reduce database query
This commit is contained in:
parent
5e61c60f85
commit
87c8f9ff95
8 changed files with 114 additions and 123 deletions
|
@ -350,7 +350,15 @@ export default defineComponent({
|
||||||
|
|
||||||
capture(withHandler = false) {
|
capture(withHandler = false) {
|
||||||
if (this.$i) {
|
if (this.$i) {
|
||||||
this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id });
|
this.connection.send('sn', { id: this.appearNote.id });
|
||||||
|
if (this.appearNote.userId !== this.$i.id) {
|
||||||
|
if (this.appearNote.mentions && this.appearNote.mentions.includes(this.$i.id)) {
|
||||||
|
this.connection.send('readMention', { id: this.appearNote.id });
|
||||||
|
}
|
||||||
|
if (this.appearNote.visibleUserIds && this.appearNote.visibleUserIds.includes(this.$i.id)) {
|
||||||
|
this.connection.send('readSpecifiedNote', { id: this.appearNote.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated);
|
if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -325,7 +325,15 @@ export default defineComponent({
|
||||||
|
|
||||||
capture(withHandler = false) {
|
capture(withHandler = false) {
|
||||||
if (this.$i) {
|
if (this.$i) {
|
||||||
this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id });
|
this.connection.send('sn', { id: this.appearNote.id });
|
||||||
|
if (this.appearNote.userId !== this.$i.id) {
|
||||||
|
if (this.appearNote.mentions && this.appearNote.mentions.includes(this.$i.id)) {
|
||||||
|
this.connection.send('readMention', { id: this.appearNote.id });
|
||||||
|
}
|
||||||
|
if (this.appearNote.visibleUserIds && this.appearNote.visibleUserIds.includes(this.$i.id)) {
|
||||||
|
this.connection.send('readSpecifiedNote', { id: this.appearNote.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated);
|
if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -325,7 +325,15 @@ export default defineComponent({
|
||||||
|
|
||||||
capture(withHandler = false) {
|
capture(withHandler = false) {
|
||||||
if (this.$i) {
|
if (this.$i) {
|
||||||
this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id });
|
this.connection.send('sn', { id: this.appearNote.id });
|
||||||
|
if (this.appearNote.userId !== this.$i.id) {
|
||||||
|
if (this.appearNote.mentions && this.appearNote.mentions.includes(this.$i.id)) {
|
||||||
|
this.connection.send('readMention', { id: this.appearNote.id });
|
||||||
|
}
|
||||||
|
if (this.appearNote.visibleUserIds && this.appearNote.visibleUserIds.includes(this.$i.id)) {
|
||||||
|
this.connection.send('readSpecifiedNote', { id: this.appearNote.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated);
|
if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import { ID } from '../../../../misc/cafy-id';
|
import { ID } from '../../../../misc/cafy-id';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import read from '../../../../services/note/read';
|
|
||||||
import { Notes, Followings } from '../../../../models';
|
import { Notes, Followings } from '../../../../models';
|
||||||
import { generateVisibilityQuery } from '../../common/generate-visibility-query';
|
import { generateVisibilityQuery } from '../../common/generate-visibility-query';
|
||||||
import { generateMutedUserQuery } from '../../common/generate-muted-user-query';
|
import { generateMutedUserQuery } from '../../common/generate-muted-user-query';
|
||||||
import { makePaginationQuery } from '../../common/make-pagination-query';
|
import { makePaginationQuery } from '../../common/make-pagination-query';
|
||||||
import { Brackets } from 'typeorm';
|
import { Brackets } from 'typeorm';
|
||||||
|
import { readMention } from '../../../../services/note/read-mention';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
|
@ -79,9 +79,7 @@ export default define(meta, async (ps, user) => {
|
||||||
|
|
||||||
const mentions = await query.take(ps.limit!).getMany();
|
const mentions = await query.take(ps.limit!).getMany();
|
||||||
|
|
||||||
for (const note of mentions) {
|
readMention(user.id, mentions.map(n => n.id));
|
||||||
read(user.id, note.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Notes.packMany(mentions, user);
|
return await Notes.packMany(mentions, user);
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,6 @@ import autobind from 'autobind-decorator';
|
||||||
import * as websocket from 'websocket';
|
import * as websocket from 'websocket';
|
||||||
import { readNotification } from '../common/read-notification';
|
import { readNotification } from '../common/read-notification';
|
||||||
import call from '../call';
|
import call from '../call';
|
||||||
import readNote from '../../../services/note/read';
|
|
||||||
import Channel from './channel';
|
import Channel from './channel';
|
||||||
import channels from './channels';
|
import channels from './channels';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
@ -14,6 +13,8 @@ import { AccessToken } from '../../../models/entities/access-token';
|
||||||
import { UserProfile } from '../../../models/entities/user-profile';
|
import { UserProfile } from '../../../models/entities/user-profile';
|
||||||
import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '../../../services/stream';
|
import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '../../../services/stream';
|
||||||
import { UserGroup } from '../../../models/entities/user-group';
|
import { UserGroup } from '../../../models/entities/user-group';
|
||||||
|
import { readMention } from '../../../services/note/read-mention';
|
||||||
|
import { readSpecifiedNote } from '../../../services/note/read-specified-note';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main stream connection
|
* Main stream connection
|
||||||
|
@ -86,9 +87,10 @@ export default class Connection {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'api': this.onApiRequest(body); break;
|
case 'api': this.onApiRequest(body); break;
|
||||||
case 'readNotification': this.onReadNotification(body); break;
|
case 'readNotification': this.onReadNotification(body); break;
|
||||||
case 'subNote': this.onSubscribeNote(body, true); break;
|
case 'readMention': this.onReadMention(body); break;
|
||||||
case 'sn': this.onSubscribeNote(body, true); break; // alias
|
case 'readSpecifiedNote': this.onReadSpecifiedNote(body); break;
|
||||||
case 's': this.onSubscribeNote(body, false); break;
|
case 'subNote': this.onSubscribeNote(body); break;
|
||||||
|
case 'sn': this.onSubscribeNote(body); break; // alias
|
||||||
case 'unsubNote': this.onUnsubscribeNote(body); break;
|
case 'unsubNote': this.onUnsubscribeNote(body); break;
|
||||||
case 'un': this.onUnsubscribeNote(body); break; // alias
|
case 'un': this.onUnsubscribeNote(body); break; // alias
|
||||||
case 'connect': this.onChannelConnectRequested(body); break;
|
case 'connect': this.onChannelConnectRequested(body); break;
|
||||||
|
@ -141,11 +143,31 @@ export default class Connection {
|
||||||
readNotification(this.user!.id, [payload.id]);
|
readNotification(this.user!.id, [payload.id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
private onReadMention(payload: any) {
|
||||||
|
if (!payload.id) return;
|
||||||
|
if (this.user) {
|
||||||
|
// TODO: ある程度まとめてreadMentionするようにする
|
||||||
|
// 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadMentionに渡すような実装にする
|
||||||
|
readMention(this.user.id, [payload.id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
private onReadSpecifiedNote(payload: any) {
|
||||||
|
if (!payload.id) return;
|
||||||
|
if (this.user) {
|
||||||
|
// TODO: ある程度まとめてreadSpecifiedNoteするようにする
|
||||||
|
// 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadSpecifiedNoteに渡すような実装にする
|
||||||
|
readSpecifiedNote(this.user.id, [payload.id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 投稿購読要求時
|
* 投稿購読要求時
|
||||||
*/
|
*/
|
||||||
@autobind
|
@autobind
|
||||||
private onSubscribeNote(payload: any, read: boolean) {
|
private onSubscribeNote(payload: any) {
|
||||||
if (!payload.id) return;
|
if (!payload.id) return;
|
||||||
|
|
||||||
if (this.subscribingNotes[payload.id] == null) {
|
if (this.subscribingNotes[payload.id] == null) {
|
||||||
|
@ -157,12 +179,6 @@ export default class Connection {
|
||||||
if (this.subscribingNotes[payload.id] === 1) {
|
if (this.subscribingNotes[payload.id] === 1) {
|
||||||
this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage);
|
this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.user && read) {
|
|
||||||
// TODO: クライアントでタイムライン読み込みなどすると、一度に大量のreadNoteが発生しクエリ数がすごいことになるので、ある程度まとめてreadNoteするようにする
|
|
||||||
// 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadNoteに渡すような実装にする
|
|
||||||
readNote(this.user.id, payload.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
29
src/services/note/read-mention.ts
Normal file
29
src/services/note/read-mention.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { publishMainStream } from '../stream';
|
||||||
|
import { Note } from '../../models/entities/note';
|
||||||
|
import { User } from '../../models/entities/user';
|
||||||
|
import { NoteUnreads } from '../../models';
|
||||||
|
import { In } from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark a mention note as read
|
||||||
|
*/
|
||||||
|
export async function readMention(
|
||||||
|
userId: User['id'],
|
||||||
|
noteIds: Note['id'][]
|
||||||
|
) {
|
||||||
|
// Remove the records
|
||||||
|
await NoteUnreads.delete({
|
||||||
|
userId: userId,
|
||||||
|
noteId: In(noteIds),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mentionsCount = await NoteUnreads.count({
|
||||||
|
userId: userId,
|
||||||
|
isMentioned: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mentionsCount === 0) {
|
||||||
|
// 全て既読になったイベントを発行
|
||||||
|
publishMainStream(userId, 'readAllUnreadMentions');
|
||||||
|
}
|
||||||
|
}
|
29
src/services/note/read-specified-note.ts
Normal file
29
src/services/note/read-specified-note.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { publishMainStream } from '../stream';
|
||||||
|
import { Note } from '../../models/entities/note';
|
||||||
|
import { User } from '../../models/entities/user';
|
||||||
|
import { NoteUnreads } from '../../models';
|
||||||
|
import { In } from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark a specified note as read
|
||||||
|
*/
|
||||||
|
export async function readSpecifiedNote(
|
||||||
|
userId: User['id'],
|
||||||
|
noteIds: Note['id'][]
|
||||||
|
) {
|
||||||
|
// Remove the records
|
||||||
|
await NoteUnreads.delete({
|
||||||
|
userId: userId,
|
||||||
|
noteId: In(noteIds),
|
||||||
|
});
|
||||||
|
|
||||||
|
const specifiedCount = await NoteUnreads.count({
|
||||||
|
userId: userId,
|
||||||
|
isSpecified: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (specifiedCount === 0) {
|
||||||
|
// 全て既読になったイベントを発行
|
||||||
|
publishMainStream(userId, 'readAllUnreadSpecifiedNotes');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,105 +0,0 @@
|
||||||
import { publishMainStream } from '../stream';
|
|
||||||
import { Note } from '../../models/entities/note';
|
|
||||||
import { User } from '../../models/entities/user';
|
|
||||||
import { NoteUnreads, Antennas, AntennaNotes, Users } from '../../models';
|
|
||||||
import { Not, IsNull } from 'typeorm';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark a note as read
|
|
||||||
*/
|
|
||||||
export default async function(
|
|
||||||
userId: User['id'],
|
|
||||||
noteId: Note['id']
|
|
||||||
) {
|
|
||||||
async function careNoteUnreads() {
|
|
||||||
const exist = await NoteUnreads.findOne({
|
|
||||||
userId: userId,
|
|
||||||
noteId: noteId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!exist) return;
|
|
||||||
|
|
||||||
// Remove the record
|
|
||||||
await NoteUnreads.delete({
|
|
||||||
userId: userId,
|
|
||||||
noteId: noteId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (exist.isMentioned) {
|
|
||||||
NoteUnreads.count({
|
|
||||||
userId: userId,
|
|
||||||
isMentioned: true
|
|
||||||
}).then(mentionsCount => {
|
|
||||||
if (mentionsCount === 0) {
|
|
||||||
// 全て既読になったイベントを発行
|
|
||||||
publishMainStream(userId, 'readAllUnreadMentions');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exist.isSpecified) {
|
|
||||||
NoteUnreads.count({
|
|
||||||
userId: userId,
|
|
||||||
isSpecified: true
|
|
||||||
}).then(specifiedCount => {
|
|
||||||
if (specifiedCount === 0) {
|
|
||||||
// 全て既読になったイベントを発行
|
|
||||||
publishMainStream(userId, 'readAllUnreadSpecifiedNotes');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exist.noteChannelId) {
|
|
||||||
NoteUnreads.count({
|
|
||||||
userId: userId,
|
|
||||||
noteChannelId: Not(IsNull())
|
|
||||||
}).then(channelNoteCount => {
|
|
||||||
if (channelNoteCount === 0) {
|
|
||||||
// 全て既読になったイベントを発行
|
|
||||||
publishMainStream(userId, 'readAllChannels');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function careAntenna() {
|
|
||||||
const beforeUnread = await Users.getHasUnreadAntenna(userId);
|
|
||||||
if (!beforeUnread) return;
|
|
||||||
|
|
||||||
const antennas = await Antennas.find({ userId });
|
|
||||||
|
|
||||||
await Promise.all(antennas.map(async antenna => {
|
|
||||||
const countBefore = await AntennaNotes.count({
|
|
||||||
antennaId: antenna.id,
|
|
||||||
read: false
|
|
||||||
});
|
|
||||||
|
|
||||||
if (countBefore === 0) return;
|
|
||||||
|
|
||||||
await AntennaNotes.update({
|
|
||||||
antennaId: antenna.id,
|
|
||||||
noteId: noteId
|
|
||||||
}, {
|
|
||||||
read: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const countAfter = await AntennaNotes.count({
|
|
||||||
antennaId: antenna.id,
|
|
||||||
read: false
|
|
||||||
});
|
|
||||||
|
|
||||||
if (countAfter === 0) {
|
|
||||||
publishMainStream(userId, 'readAntenna', antenna);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
Users.getHasUnreadAntenna(userId).then(unread => {
|
|
||||||
if (!unread) {
|
|
||||||
publishMainStream(userId, 'readAllAntennas');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
careNoteUnreads();
|
|
||||||
careAntenna();
|
|
||||||
}
|
|
Loading…
Reference in a new issue