diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 4a3454c36f..a52f35cde6 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -6,6 +6,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository, FollowingsRepository } from '@/models/_.js'; +import { MiNote } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -61,37 +62,52 @@ export default class extends Endpoint { // eslint- private readonly activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), - ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => qb - .where(':meIdAsList <@ note.mentions') - .orWhere(':meIdAsList <@ note.visibleUserIds'))) - .setParameters({ meIdAsList: [me.id] }) - // Avoid scanning primary key index - .orderBy('CONCAT(note.id)', 'DESC') + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + .innerJoin(qb => { + qb + .select('note.id', 'id') + .from(qbb => qbb + .select('note.id', 'id') + .from(MiNote, 'note') + .where(new Brackets(qbbb => qbbb + // DM to me + .orWhere(':meIdAsList <@ note.visibleUserIds') + // Mentions me + .orWhere(':meIdAsList <@ note.mentions'), + )) + .setParameters({ meIdAsList: [me.id] }) + , 'source') + .innerJoin(MiNote, 'note', 'note.id = source.id'); + + // Mentioned or visible users can always access + //this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(qb); + this.queryService.generateMutedUserQueryForNotes(qb, me); + this.queryService.generateMutedNoteThreadQuery(qb, me); + this.queryService.generateBlockedUserQueryForNotes(qb, me); + // A renote can't mention a user, so it will never appear here anyway. + //this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + + if (ps.visibility) { + qb.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); + } + + if (ps.following) { + this.queryService + .andFollowingUser(qb, ':meId', 'note.userId') + .setParameters({ meId: me.id }); + } + + return qb; + }, 'source', 'source.id = note.id') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') - .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .limit(ps.limit); - this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateBlockedHostQueryForNote(query); - this.queryService.generateMutedUserQueryForNotes(query, me); - this.queryService.generateMutedNoteThreadQuery(query, me); - this.queryService.generateBlockedUserQueryForNotes(query, me); - // A renote can't mention a user, so it will never appear here anyway. - //this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - - if (ps.visibility) { - query.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); - } - - if (ps.following) { - this.queryService.andFollowingUser(query, ':meId', 'note.userId'); - } - - const mentions = await query.limit(ps.limit).getMany(); + const mentions = await query.getMany(); process.nextTick(() => { this.activeUsersChart.read(me);