mirror of
https://codeberg.org/yeentown/barkey.git
synced 2025-07-07 12:36:57 +00:00
additional fixes and cleanup to all note endpoints
This commit is contained in:
parent
65983d0030
commit
05d7aa0b91
19 changed files with 282 additions and 202 deletions
|
@ -7,6 +7,7 @@ import { DI } from '@/di-symbols.js';
|
|||
import type { LatestNotesRepository, NotesRepository } from '@/models/_.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import Logger from '@/logger.js';
|
||||
import { QueryService } from './QueryService.js';
|
||||
|
||||
@Injectable()
|
||||
export class LatestNoteService {
|
||||
|
@ -14,11 +15,12 @@ export class LatestNoteService {
|
|||
|
||||
constructor(
|
||||
@Inject(DI.notesRepository)
|
||||
private notesRepository: NotesRepository,
|
||||
private readonly notesRepository: NotesRepository,
|
||||
|
||||
@Inject(DI.latestNotesRepository)
|
||||
private latestNotesRepository: LatestNotesRepository,
|
||||
private readonly latestNotesRepository: LatestNotesRepository,
|
||||
|
||||
private readonly queryService: QueryService,
|
||||
loggerService: LoggerService,
|
||||
) {
|
||||
this.logger = loggerService.getLogger('LatestNoteService');
|
||||
|
@ -91,7 +93,7 @@ export class LatestNoteService {
|
|||
|
||||
// Find the newest remaining note for the user.
|
||||
// We exclude DMs and pure renotes.
|
||||
const nextLatest = await this.notesRepository
|
||||
const query = this.notesRepository
|
||||
.createQueryBuilder('note')
|
||||
.select()
|
||||
.where({
|
||||
|
@ -106,18 +108,11 @@ export class LatestNoteService {
|
|||
? Not(null)
|
||||
: null,
|
||||
})
|
||||
.andWhere(`
|
||||
(
|
||||
note."renoteId" IS NULL
|
||||
OR note.text IS NOT NULL
|
||||
OR note.cw IS NOT NULL
|
||||
OR note."replyId" IS NOT NULL
|
||||
OR note."hasPoll"
|
||||
OR note."fileIds" != '{}'
|
||||
)
|
||||
`)
|
||||
.orderBy({ id: 'DESC' })
|
||||
.getOne();
|
||||
.orderBy({ id: 'DESC' });
|
||||
|
||||
this.queryService.andIsNotRenote(query, 'note');
|
||||
|
||||
const nextLatest = await query.getOne();
|
||||
if (!nextLatest) return;
|
||||
|
||||
// Record it as the latest
|
||||
|
|
|
@ -188,15 +188,8 @@ export class QueryService {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public generateExcludedRenotesQueryForNotes<E extends ObjectLiteral>(q: SelectQueryBuilder<E>): SelectQueryBuilder<E> {
|
||||
return q
|
||||
.andWhere(new Brackets(qb => qb
|
||||
.orWhere('note.renoteId IS NULL')
|
||||
.orWhere('note.text IS NOT NULL')
|
||||
.orWhere('note.cw IS NOT NULL')
|
||||
.orWhere('note.replyId IS NOT NULL')
|
||||
.orWhere('note.hasPoll = true')
|
||||
.orWhere('note.fileIds != \'{}\'')));
|
||||
public generateExcludedRenotesQueryForNotes<Q extends WhereExpressionBuilder>(q: Q): Q {
|
||||
return this.andIsNotRenote(q, 'note');
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
@ -256,6 +249,120 @@ export class QueryService {
|
|||
return q;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds OR condition that noteProp (note ID) refers to a quote.
|
||||
* The prop should be an expression, not a raw value.
|
||||
*/
|
||||
@bindThis
|
||||
public orIsQuote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string): Q {
|
||||
return this.addIsQuote(q, noteProp, 'orWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds AND condition that noteProp (note ID) refers to a quote.
|
||||
* The prop should be an expression, not a raw value.
|
||||
*/
|
||||
@bindThis
|
||||
public andIsQuote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string): Q {
|
||||
return this.addIsQuote(q, noteProp, 'andWhere');
|
||||
}
|
||||
|
||||
private addIsQuote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string, join: 'andWhere' | 'orWhere'): Q {
|
||||
return q[join](new Brackets(qb => qb
|
||||
.andWhere(`${noteProp}.renoteId IS NOT NULL`)
|
||||
.andWhere(new Brackets(qbb => qbb
|
||||
.orWhere(`${noteProp}.text IS NOT NULL`)
|
||||
.orWhere(`${noteProp}.cw IS NOT NULL`)
|
||||
.orWhere(`${noteProp}.replyId IS NOT NULL`)
|
||||
.orWhere(`${noteProp}.hasPoll = true`)
|
||||
.orWhere(`${noteProp}.fileIds != '{}'`)))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds OR condition that noteProp (note ID) does not refer to a quote.
|
||||
* The prop should be an expression, not a raw value.
|
||||
*/
|
||||
@bindThis
|
||||
public orIsNotQuote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string): Q {
|
||||
return this.addIsNotQuote(q, noteProp, 'orWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds AND condition that noteProp (note ID) does not refer to a quote.
|
||||
* The prop should be an expression, not a raw value.
|
||||
*/
|
||||
@bindThis
|
||||
public andIsNotQuote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string): Q {
|
||||
return this.addIsNotQuote(q, noteProp, 'andWhere');
|
||||
}
|
||||
|
||||
private addIsNotQuote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string, join: 'andWhere' | 'orWhere'): Q {
|
||||
return q[join](new Brackets(qb => qb
|
||||
.orWhere(`${noteProp}.renoteId IS NULL`)
|
||||
.orWhere(new Brackets(qb => qb
|
||||
.andWhere(`${noteProp}.text IS NULL`)
|
||||
.andWhere(`${noteProp}.cw IS NULL`)
|
||||
.andWhere(`${noteProp}.replyId IS NULL`)
|
||||
.andWhere(`${noteProp}.hasPoll = false`)
|
||||
.andWhere(`${noteProp}.fileIds = '{}'`)))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds OR condition that noteProp (note ID) refers to a renote.
|
||||
* The prop should be an expression, not a raw value.
|
||||
*/
|
||||
@bindThis
|
||||
public orIsRenote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string): Q {
|
||||
return this.addIsRenote(q, noteProp, 'orWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds AND condition that noteProp (note ID) refers to a renote.
|
||||
* The prop should be an expression, not a raw value.
|
||||
*/
|
||||
@bindThis
|
||||
public andIsRenote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string): Q {
|
||||
return this.addIsRenote(q, noteProp, 'andWhere');
|
||||
}
|
||||
|
||||
private addIsRenote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string, join: 'andWhere' | 'orWhere'): Q {
|
||||
return q[join](new Brackets(qb => qb
|
||||
.andWhere(`${noteProp}.renoteId IS NOT NULL`)
|
||||
.andWhere(`${noteProp}.text IS NULL`)
|
||||
.andWhere(`${noteProp}.cw IS NULL`)
|
||||
.andWhere(`${noteProp}.replyId IS NULL`)
|
||||
.andWhere(`${noteProp}.hasPoll = false`)
|
||||
.andWhere(`${noteProp}.fileIds = '{}'`)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds OR condition that noteProp (note ID) does not refer to a renote.
|
||||
* The prop should be an expression, not a raw value.
|
||||
*/
|
||||
@bindThis
|
||||
public orIsNotRenote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string): Q {
|
||||
return this.addIsNotRenote(q, noteProp, 'orWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds AND condition that noteProp (note ID) does not refer to a renote.
|
||||
* The prop should be an expression, not a raw value.
|
||||
*/
|
||||
@bindThis
|
||||
public andIsNotRenote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string): Q {
|
||||
return this.addIsNotRenote(q, noteProp, 'andWhere');
|
||||
}
|
||||
|
||||
private addIsNotRenote<Q extends WhereExpressionBuilder>(q: Q, noteProp: string, join: 'andWhere' | 'orWhere'): Q {
|
||||
return q[join](new Brackets(qb => qb
|
||||
.orWhere(`${noteProp}.renoteId IS NULL`)
|
||||
.orWhere(`${noteProp}.text IS NOT NULL`)
|
||||
.orWhere(`${noteProp}.cw IS NOT NULL`)
|
||||
.orWhere(`${noteProp}.replyId IS NOT NULL`)
|
||||
.orWhere(`${noteProp}.hasPoll = true`)
|
||||
.orWhere(`${noteProp}.fileIds != '{}'`)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds OR condition that followerProp (user ID) is following followeeProp (user ID).
|
||||
* Both props should be expressions, not raw values.
|
||||
|
|
|
@ -114,8 +114,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.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');
|
||||
|
||||
// NOTE: センシティブ除外の設定はこのエンドポイントでは無視する。
|
||||
// https://github.com/misskey-dev/misskey/pull/15346#discussion_r1929950255
|
||||
|
|
|
@ -137,12 +137,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.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('note.channel', 'channel');
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.leftJoinAndSelect('note.channel', 'channel')
|
||||
.limit(ps.limit);
|
||||
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
|
@ -159,6 +161,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
}
|
||||
//#endregion
|
||||
|
||||
return await query.limit(ps.limit).getMany();
|
||||
return await query.getMany();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,10 +81,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchFile);
|
||||
}
|
||||
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId);
|
||||
query.andWhere(':file <@ note.fileIds', { file: [file.id] });
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||
.andWhere(':file <@ note.fileIds', { file: [file.id] })
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.limit(ps.limit);
|
||||
|
||||
const notes = await query.limit(ps.limit).getMany();
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
|
||||
const notes = await query.getMany();
|
||||
|
||||
return await this.noteEntityService.packMany(notes, me, {
|
||||
detail: true,
|
||||
|
|
|
@ -64,7 +64,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.limit(ps.limit);
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
if (me) {
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
}
|
||||
|
||||
if (ps.local) {
|
||||
query.andWhere('note.userHost IS NULL');
|
||||
|
@ -75,7 +84,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
}
|
||||
|
||||
if (ps.renote !== undefined) {
|
||||
query.andWhere(ps.renote ? 'note.renoteId IS NOT NULL' : 'note.renoteId IS NULL');
|
||||
if (ps.renote) {
|
||||
this.queryService.andIsRenote(query, 'note');
|
||||
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
}
|
||||
} else {
|
||||
this.queryService.andIsNotRenote(query, 'note');
|
||||
}
|
||||
}
|
||||
|
||||
if (ps.withFiles !== undefined) {
|
||||
|
@ -91,7 +108,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
// query.isBot = bot;
|
||||
//}
|
||||
|
||||
const notes = await query.limit(ps.limit).getMany();
|
||||
const notes = await query.getMany();
|
||||
|
||||
return await this.noteEntityService.packMany(notes);
|
||||
});
|
||||
|
|
|
@ -82,8 +82,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.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 subquery mess teaches postgres how to use the right indexes.
|
||||
// Using WHERE or ON conditions causes a fallback to full sequence scan, which times out.
|
||||
|
@ -114,7 +115,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
}
|
||||
//#endregion
|
||||
|
||||
const timeline = await query.limit(ps.limit).getMany();
|
||||
const timeline = await query.getMany();
|
||||
|
||||
if (me) {
|
||||
process.nextTick(() => {
|
||||
|
|
|
@ -57,26 +57,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
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('note.replyId = :noteId', { noteId: ps.noteId });
|
||||
qb.orWhere('note.replyId = :noteId');
|
||||
|
||||
if (ps.showQuotes) {
|
||||
qb.orWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.renoteId = :noteId', { noteId: ps.noteId })
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.text IS NOT NULL')
|
||||
.orWhere('note.fileIds != \'{}\'')
|
||||
.orWhere('note.hasPoll = TRUE');
|
||||
}));
|
||||
}));
|
||||
qb.orWhere(new Brackets(qbb => this.queryService
|
||||
.andIsQuote(qbb, 'note')
|
||||
.andWhere('note.renoteId = :noteId'),
|
||||
));
|
||||
}
|
||||
}))
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.setParameters({ noteId: ps.noteId })
|
||||
.limit(ps.limit);
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
|
@ -85,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
}
|
||||
|
||||
const notes = await query.limit(ps.limit).getMany();
|
||||
const notes = await query.getMany();
|
||||
|
||||
return await this.noteEntityService.packMany(notes, me);
|
||||
});
|
||||
|
|
|
@ -87,7 +87,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
const query = this.notesRepository
|
||||
.createQueryBuilder('note')
|
||||
.setParameter('me', me.id)
|
||||
.setParameters({ meId: me.id })
|
||||
|
||||
// Limit to latest notes
|
||||
.innerJoin(
|
||||
|
@ -130,8 +130,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.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')
|
||||
|
||||
// Exclude channel notes
|
||||
.andWhere({ channelId: IsNull() })
|
||||
|
@ -177,14 +177,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
* Limit to followers (they follow us)
|
||||
*/
|
||||
function addFollower<T extends SelectQueryBuilder<ObjectLiteral>>(query: T): T {
|
||||
return query.innerJoin(MiFollowing, 'follower', 'follower."followerId" = latest.user_id AND follower."followeeId" = :me');
|
||||
return query.innerJoin(MiFollowing, 'follower', 'follower."followerId" = latest.user_id AND follower."followeeId" = :meId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit to followees (we follow them)
|
||||
*/
|
||||
function addFollowee<T extends SelectQueryBuilder<ObjectLiteral>>(query: T): T {
|
||||
return query.innerJoin(MiFollowing, 'followee', 'followee."followerId" = :me AND followee."followeeId" = latest.user_id');
|
||||
return query.innerJoin(MiFollowing, 'followee', 'followee."followerId" = :meId AND followee."followeeId" = latest.user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -82,8 +82,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.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');
|
||||
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
|
|
|
@ -197,51 +197,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
withBots: boolean,
|
||||
withRenotes: boolean,
|
||||
}, me: MiLocalUser) {
|
||||
const followees = await this.userFollowingService.getFollowees(me.id);
|
||||
const followingChannels = await this.channelFollowingsRepository.find({
|
||||
where: {
|
||||
followerId: me.id,
|
||||
},
|
||||
});
|
||||
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||
.andWhere(new Brackets(qb => {
|
||||
if (followees.length > 0) {
|
||||
const meOrFolloweeIds = [me.id, ...followees.map(f => f.followeeId)];
|
||||
qb.where('note.userId IN (:...meOrFolloweeIds)', { meOrFolloweeIds: meOrFolloweeIds });
|
||||
qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)');
|
||||
} else {
|
||||
qb.where('note.userId = :meId', { meId: me.id });
|
||||
qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)');
|
||||
}
|
||||
}))
|
||||
// 1. by a user I follow, 2. a public local post, 3. my own post
|
||||
.andWhere(new Brackets(qb => this.queryService
|
||||
.orFollowingUser(qb, ':meId', 'note.userId')
|
||||
.orWhere(new Brackets(qbb => qbb
|
||||
.andWhere('note.visibility = \'public\'')
|
||||
.andWhere('note.userHost IS NULL')))
|
||||
.orWhere(':meId = note.userId')))
|
||||
// 1. in a channel I follow, 2. not in a channel
|
||||
.andWhere(new Brackets(qb => this.queryService
|
||||
.orFollowingChannel(qb, ':meId', 'note.channelId')
|
||||
.orWhere('note.channelId IS NULL')))
|
||||
.setParameters({ meId: me.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');
|
||||
|
||||
if (followingChannels.length > 0) {
|
||||
const followingChannelIds = followingChannels.map(x => x.followeeId);
|
||||
|
||||
query.andWhere(new Brackets(qb => {
|
||||
qb.where('note.channelId IN (:...followingChannelIds)', { followingChannelIds });
|
||||
qb.orWhere('note.channelId IS NULL');
|
||||
}));
|
||||
} else {
|
||||
query.andWhere('note.channelId IS NULL');
|
||||
}
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.limit(ps.limit);
|
||||
|
||||
if (!ps.withReplies) {
|
||||
query.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.replyId IS NULL') // 返信ではない
|
||||
.orWhere(new Brackets(qb => {
|
||||
qb // 返信だけど投稿者自身への返信
|
||||
.where('note.replyId IS NOT NULL')
|
||||
.andWhere('note.replyUserId = note.userId');
|
||||
}));
|
||||
}));
|
||||
query
|
||||
// 1. Not a reply, 2. a self-reply
|
||||
.andWhere(new Brackets(qb => qb
|
||||
.orWhere('note.replyId IS NULL') // 返信ではない
|
||||
.orWhere('note.replyUserId = note.userId')));
|
||||
}
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
|
@ -263,6 +244,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
}
|
||||
//#endregion
|
||||
|
||||
return await query.limit(ps.limit).getMany();
|
||||
return await query.getMany();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,8 +168,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.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);
|
||||
|
||||
if (!ps.withReplies) {
|
||||
query
|
||||
// 1. Not a reply, 2. a self-reply
|
||||
.andWhere(new Brackets(qb => qb
|
||||
.orWhere('note.replyId IS NULL') // 返信ではない
|
||||
.orWhere('note.replyUserId = note.userId')));
|
||||
}
|
||||
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
|
@ -190,18 +199,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
}
|
||||
|
||||
if (!ps.withReplies) {
|
||||
query.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.replyId IS NULL') // 返信ではない
|
||||
.orWhere(new Brackets(qb => {
|
||||
qb // 返信だけど投稿者自身への返信
|
||||
.where('note.replyId IS NOT NULL')
|
||||
.andWhere('note.replyUserId = note.userId');
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
return await query.limit(ps.limit).getMany();
|
||||
return await query.getMany();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ export const paramDef = {
|
|||
type: 'object',
|
||||
properties: {
|
||||
noteId: { type: 'string', format: 'misskey:id' },
|
||||
userId: { type: "string", format: "misskey:id" },
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
|
@ -81,19 +81,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
if (ps.userId) {
|
||||
query.andWhere("user.id = :userId", { userId: ps.userId });
|
||||
query.andWhere('user.id = :userId', { userId: ps.userId });
|
||||
}
|
||||
|
||||
if (ps.quote) {
|
||||
query.andWhere("note.text IS NOT NULL");
|
||||
this.queryService.andIsQuote(query, 'note');
|
||||
} else {
|
||||
query.andWhere("note.text IS NULL");
|
||||
this.queryService.andIsRenote(query, 'note');
|
||||
}
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
}
|
||||
|
||||
const renotes = await query.limit(ps.limit).getMany();
|
||||
|
||||
|
|
|
@ -59,14 +59,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.limit(ps.limit);
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
}
|
||||
|
||||
const timeline = await query.limit(ps.limit).getMany();
|
||||
const timeline = await query.getMany();
|
||||
|
||||
return await this.noteEntityService.packMany(timeline, me);
|
||||
});
|
||||
|
|
|
@ -12,8 +12,6 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
|||
import { QueryService } from '@/core/QueryService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes', 'hashtags'],
|
||||
|
@ -82,19 +80,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
private noteEntityService: NoteEntityService,
|
||||
private queryService: QueryService,
|
||||
private cacheService: CacheService,
|
||||
private utilityService: UtilityService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||
.andWhere("note.visibility IN ('public', 'home')") // keep in sync with NoteCreateService call to `hashtagService.updateHashtags()`
|
||||
.andWhere(new Brackets(qb => qb
|
||||
.orWhere('note.visibility = \'public\'')
|
||||
.orWhere('note.visibility = \'home\''))) // keep in sync with NoteCreateService call to `hashtagService.updateHashtags()`
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
if (!this.serverSettings.enableBotTrending) query.andWhere('user.isBot = FALSE');
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.limit(ps.limit);
|
||||
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
|
@ -102,7 +99,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
|
||||
const followings = me ? await this.cacheService.userFollowingsCache.fetch(me.id) : {};
|
||||
if (!this.serverSettings.enableBotTrending) query.andWhere('user.isBot = FALSE');
|
||||
|
||||
try {
|
||||
if (ps.tag) {
|
||||
|
@ -135,9 +132,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
if (ps.renote != null) {
|
||||
if (ps.renote) {
|
||||
query.andWhere('note.renoteId IS NOT NULL');
|
||||
this.queryService.andIsRenote(query, 'note');
|
||||
} else {
|
||||
query.andWhere('note.renoteId IS NULL');
|
||||
this.queryService.andIsNotRenote(query, 'note');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,16 +151,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
}
|
||||
|
||||
// Search notes
|
||||
let notes = await query.limit(ps.limit).getMany();
|
||||
|
||||
notes = notes.filter(note => {
|
||||
if (note.user?.isSilenced && me && followings && note.userId !== me.id && !followings[note.userId]) return false;
|
||||
if (note.user?.isSuspended) return false;
|
||||
if (note.userHost) {
|
||||
if (!this.utilityService.isFederationAllowedHost(note.userHost)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
const notes = await query.getMany();
|
||||
|
||||
return await this.noteEntityService.packMany(notes, me);
|
||||
});
|
||||
|
|
|
@ -142,11 +142,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private async getFromDb(ps: { untilId: string | null; sinceId: string | null; limit: number; withFiles: boolean; withRenotes: boolean; withBots: boolean; }, me: MiLocalUser) {
|
||||
//#region Construct query
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
// 1. in a channel I follow, 2. my own post, 3. by a user I follow
|
||||
.andWhere(new Brackets(qb => this.queryService
|
||||
.orFollowingChannel(qb, ':meId', 'note.channelId')
|
||||
|
@ -160,10 +155,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.orWhere('note.replyId IS NULL') // 返信ではない
|
||||
.orWhere('note.replyUserId = note.userId')))
|
||||
.setParameters({ meId: me.id })
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.limit(ps.limit);
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
|
||||
|
|
|
@ -154,32 +154,25 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
//#region Construct query
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||
.innerJoin(this.userListMembershipsRepository.metadata.targetName, 'userListMemberships', 'userListMemberships.userId = note.userId')
|
||||
.andWhere('userListMemberships.userListId = :userListId', { userListId: list.id })
|
||||
.andWhere('note.channelId IS NULL') // チャンネルノートではない
|
||||
.andWhere(new Brackets(qb => qb
|
||||
// 返信ではない
|
||||
.orWhere('note.replyId IS NULL')
|
||||
// 返信だけど投稿者自身への返信
|
||||
.orWhere('note.replyUserId = note.userId')
|
||||
// 返信だけど自分宛ての返信
|
||||
.orWhere('note.replyUserId = :meId')
|
||||
// 返信だけどwithRepliesがtrueの場合
|
||||
.orWhere('userListMemberships.withReplies = true'),
|
||||
))
|
||||
.setParameters({ meId: me.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')
|
||||
.andWhere('userListMemberships.userListId = :userListId', { userListId: list.id })
|
||||
.andWhere('note.channelId IS NULL') // チャンネルノートではない
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.replyId IS NULL') // 返信ではない
|
||||
.orWhere(new Brackets(qb => {
|
||||
qb // 返信だけど投稿者自身への返信
|
||||
.where('note.replyId IS NOT NULL')
|
||||
.andWhere('note.replyUserId = note.userId');
|
||||
}))
|
||||
.orWhere(new Brackets(qb => {
|
||||
qb // 返信だけど自分宛ての返信
|
||||
.where('note.replyId IS NOT NULL')
|
||||
.andWhere('note.replyUserId = :meId', { meId: me.id });
|
||||
}))
|
||||
.orWhere(new Brackets(qb => {
|
||||
qb // 返信だけどwithRepliesがtrueの場合
|
||||
.where('note.replyId IS NOT NULL')
|
||||
.andWhere('userListMemberships.withReplies = true');
|
||||
}));
|
||||
}));
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.limit(ps.limit);
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
|
@ -192,12 +185,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
if (!ps.withRenotes) {
|
||||
this.queryService.generateExcludedRenotesQueryForNotes(query);
|
||||
} else if (me) {
|
||||
} else {
|
||||
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
return await query.limit(ps.limit).getMany();
|
||||
return await query.getMany();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,10 +107,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.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');
|
||||
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
|
|
|
@ -205,7 +205,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('note.channel', 'channel')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.limit(ps.limit);
|
||||
|
||||
if (ps.withChannelNotes) {
|
||||
if (!isSelf) query.andWhere(new Brackets(qb => {
|
||||
|
@ -230,26 +231,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
if (!ps.withRenotes && !ps.withQuotes) {
|
||||
query.andWhere('note.renoteId IS NULL');
|
||||
} else if (!ps.withRenotes) {
|
||||
query.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('note.userId != :userId', { userId: ps.userId });
|
||||
qb.orWhere('note.renoteId IS NULL');
|
||||
qb.orWhere('note.text IS NOT NULL');
|
||||
qb.orWhere('note.fileIds != \'{}\'');
|
||||
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
|
||||
}));
|
||||
this.queryService.andIsNotRenote(query, 'note');
|
||||
} else if (!ps.withQuotes) {
|
||||
query.andWhere(`
|
||||
(
|
||||
note."renoteId" IS NULL
|
||||
OR (
|
||||
note.text IS NULL
|
||||
AND note.cw IS NULL
|
||||
AND note."replyId" IS NULL
|
||||
AND note."hasPoll" IS FALSE
|
||||
AND note."fileIds" = '{}'
|
||||
)
|
||||
)
|
||||
`);
|
||||
this.queryService.andIsNotQuote(query, 'note');
|
||||
}
|
||||
|
||||
if (!ps.withRepliesToOthers && !ps.withRepliesToSelf) {
|
||||
|
@ -268,6 +252,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
query.andWhere('"user"."isBot" = false');
|
||||
}
|
||||
|
||||
return await query.limit(ps.limit).getMany();
|
||||
return await query.getMany();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue