fix andWhere/orWhere in QueryService.ts

This commit is contained in:
Hazelnoot 2025-06-02 22:51:10 -04:00
parent 51d9b1c8c5
commit 0b9c0a6bc7

View file

@ -81,11 +81,11 @@ export class QueryService {
// 投稿の引用元の作者にブロックされていない // 投稿の引用元の作者にブロックされていない
return this.excludeBlockingUser(q, 'note.userId', ':meId') return this.excludeBlockingUser(q, 'note.userId', ':meId')
.andWhere(new Brackets(qb => { .andWhere(new Brackets(qb => {
this.excludeBlockingUser(qb, 'note.replyUserId', ':meId') this.excludeBlockingUser(qb, 'note.replyUserId', ':meId', 'orWhere')
.orWhere('note.replyUserId IS NULL'); .orWhere('note.replyUserId IS NULL');
})) }))
.andWhere(new Brackets(qb => { .andWhere(new Brackets(qb => {
this.excludeBlockingUser(qb, 'note.renoteUserId', ':meId') this.excludeBlockingUser(qb, 'note.renoteUserId', ':meId', 'orWhere')
.orWhere('note.renoteUserId IS NULL'); .orWhere('note.renoteUserId IS NULL');
})) }))
.setParameters({ meId: me.id }); .setParameters({ meId: me.id });
@ -102,7 +102,7 @@ export class QueryService {
public generateMutedNoteThreadQuery<E extends ObjectLiteral>(q: SelectQueryBuilder<E>, me: { id: MiUser['id'] }): SelectQueryBuilder<E> { public generateMutedNoteThreadQuery<E extends ObjectLiteral>(q: SelectQueryBuilder<E>, me: { id: MiUser['id'] }): SelectQueryBuilder<E> {
return this.excludeMutingThread(q, ':meId', 'note.id') return this.excludeMutingThread(q, ':meId', 'note.id')
.andWhere(new Brackets(qb => { .andWhere(new Brackets(qb => {
this.excludeMutingThread(qb, ':meId', 'note.threadId') this.excludeMutingThread(qb, ':meId', 'note.threadId', 'orWhere')
.orWhere('note.threadId IS NULL'); .orWhere('note.threadId IS NULL');
})) }))
.setParameters({ meId: me.id }); .setParameters({ meId: me.id });
@ -113,24 +113,24 @@ export class QueryService {
// 投稿の作者をミュートしていない かつ // 投稿の作者をミュートしていない かつ
// 投稿の返信先の作者をミュートしていない かつ // 投稿の返信先の作者をミュートしていない かつ
// 投稿の引用元の作者をミュートしていない // 投稿の引用元の作者をミュートしていない
this.excludeMutingUser(q, ':meId', 'note.userId', exclude) this.excludeMutingUser(q, ':meId', 'note.userId', 'andWhere', exclude)
.andWhere(new Brackets(qb => { .andWhere(new Brackets(qb => {
this.excludeMutingUser(qb, ':meId', 'note.replyUserId', exclude) this.excludeMutingUser(qb, ':meId', 'note.replyUserId', 'orWhere', exclude)
.orWhere('note.replyUserId IS NULL'); .orWhere('note.replyUserId IS NULL');
})) }))
.andWhere(new Brackets(qb => { .andWhere(new Brackets(qb => {
this.excludeMutingUser(qb, ':meId', 'note.renoteUserId', exclude) this.excludeMutingUser(qb, ':meId', 'note.renoteUserId', 'orWhere', exclude)
.orWhere('note.renoteUserId IS NULL'); .orWhere('note.renoteUserId IS NULL');
})); }));
// mute instances // mute instances
this.excludeMutingInstance(q, ':meId', 'note.userHost') this.excludeMutingInstance(q, ':meId', 'note.userHost', 'andWhere')
.andWhere(new Brackets(qb => { .andWhere(new Brackets(qb => {
this.excludeMutingInstance(qb, ':meId', 'note.replyUserHost') this.excludeMutingInstance(qb, ':meId', 'note.replyUserHost', 'orWhere')
.orWhere('note.replyUserHost IS NULL'); .orWhere('note.replyUserHost IS NULL');
})) }))
.andWhere(new Brackets(qb => { .andWhere(new Brackets(qb => {
this.excludeMutingInstance(qb, ':meId', 'note.renoteUserHost') this.excludeMutingInstance(qb, ':meId', 'note.renoteUserHost', 'orWhere')
.orWhere('note.renoteUserHost IS NULL'); .orWhere('note.renoteUserHost IS NULL');
})); }));
@ -231,6 +231,25 @@ export class QueryService {
return q; return q;
} }
@bindThis
public generateSilencedUserQueryForNotes<E extends ObjectLiteral>(q: SelectQueryBuilder<E>, me?: { id: MiUser['id'] } | null, userProp = 'user'): SelectQueryBuilder<E> {
if (!me) {
return q.andWhere(`${userProp}.isSilenced = false`);
}
return q
.andWhere(new Brackets(qb => {
// case 1: we are following the user
this.addFollowingUser(qb, ':meId', `${userProp}.id`, 'orWhere');
// case 2: user not silenced AND instance not silenced
qb.orWhere(new Brackets(qbb => {
this.includeInstanceWhere(qbb, `${userProp}.host`, { isSilenced: false });
qbb.andWhere(`${userProp}.isSilenced = false`);
}));
}))
.setParameters({ meId: me.id });
}
@bindThis @bindThis
public generateMatchingHostQueryForNote<E extends ObjectLiteral>(q: SelectQueryBuilder<E>, filters: FindOptionsWhere<MiInstance> | FindOptionsWhere<MiInstance>[], hostProp = 'note.userHost'): SelectQueryBuilder<E> { public generateMatchingHostQueryForNote<E extends ObjectLiteral>(q: SelectQueryBuilder<E>, filters: FindOptionsWhere<MiInstance> | FindOptionsWhere<MiInstance>[], hostProp = 'note.userHost'): SelectQueryBuilder<E> {
return this.includeInstanceWhere(q, hostProp, filters); return this.includeInstanceWhere(q, hostProp, filters);
@ -268,13 +287,13 @@ export class QueryService {
* Adds condition that followerProp (user ID) is following followeeProp (user ID). * Adds condition that followerProp (user ID) is following followeeProp (user ID).
* Both props should be expressions, not raw values. * Both props should be expressions, not raw values.
*/ */
public addFollowingUser<Q extends WhereExpressionBuilder>(q: Q, followerProp: string, followeeProp: string): Q { public addFollowingUser<Q extends WhereExpressionBuilder>(q: Q, followerProp: string, followeeProp: string, join: 'andWhere' | 'orWhere' = 'andWhere'): Q {
const followingQuery = this.followingsRepository.createQueryBuilder('following') const followingQuery = this.followingsRepository.createQueryBuilder('following')
.select('1') .select('1')
.andWhere(`following.followerId = ${followerProp}`) .andWhere(`following.followerId = ${followerProp}`)
.andWhere(`following.followeeId = ${followeeProp}`); .andWhere(`following.followeeId = ${followeeProp}`);
return q.andWhere(`EXISTS (${followingQuery.getQuery()})`, followingQuery.getParameters()); return q[join](`EXISTS (${followingQuery.getQuery()})`, followingQuery.getParameters());
}; };
/** /**
@ -282,13 +301,13 @@ export class QueryService {
* Both props should be expressions, not raw values. * Both props should be expressions, not raw values.
*/ */
@bindThis @bindThis
public excludeBlockingUser<Q extends WhereExpressionBuilder>(q: Q, blockerProp: string, blockeeProp: string): Q { public excludeBlockingUser<Q extends WhereExpressionBuilder>(q: Q, blockerProp: string, blockeeProp: string, join: 'andWhere' | 'orWhere' = 'andWhere'): Q {
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
.select('1') .select('1')
.andWhere(`blocking.blockerId = ${blockerProp}`) .andWhere(`blocking.blockerId = ${blockerProp}`)
.andWhere(`blocking.blockeeId = ${blockeeProp}`); .andWhere(`blocking.blockeeId = ${blockeeProp}`);
return q.andWhere(`NOT EXISTS (${blockingQuery.getQuery()})`, blockingQuery.getParameters()); return q[join](`NOT EXISTS (${blockingQuery.getQuery()})`, blockingQuery.getParameters());
}; };
/** /**
@ -296,7 +315,7 @@ export class QueryService {
* Both props should be expressions, not raw values. * Both props should be expressions, not raw values.
*/ */
@bindThis @bindThis
public excludeMutingUser<Q extends WhereExpressionBuilder>(q: Q, muterProp: string, muteeProp: string, exclude?: { id: MiUser['id'] }): Q { public excludeMutingUser<Q extends WhereExpressionBuilder>(q: Q, muterProp: string, muteeProp: string, join: 'andWhere' | 'orWhere' = 'andWhere', exclude?: { id: MiUser['id'] }): Q {
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
.select('1') .select('1')
.andWhere(`muting.muterId = ${muterProp}`) .andWhere(`muting.muterId = ${muterProp}`)
@ -306,20 +325,20 @@ export class QueryService {
mutingQuery.andWhere({ muteeId: Not(exclude.id) }); mutingQuery.andWhere({ muteeId: Not(exclude.id) });
} }
return q.andWhere(`NOT EXISTS (${mutingQuery.getQuery()})`, mutingQuery.getParameters()); return q[join](`NOT EXISTS (${mutingQuery.getQuery()})`, mutingQuery.getParameters());
} }
/** /**
* Adds condition that muterProp (user ID) is not muting renotes by muteeProp (user ID). * Adds condition that muterProp (user ID) is not muting renotes by muteeProp (user ID).
* Both props should be expressions, not raw values. * Both props should be expressions, not raw values.
*/ */
public excludeMutingRenote<Q extends WhereExpressionBuilder>(q: Q, muterProp: string, muteeProp: string): Q { public excludeMutingRenote<Q extends WhereExpressionBuilder>(q: Q, muterProp: string, muteeProp: string, join: 'andWhere' | 'orWhere' = 'andWhere'): Q {
const mutingQuery = this.renoteMutingsRepository.createQueryBuilder('renote_muting') const mutingQuery = this.renoteMutingsRepository.createQueryBuilder('renote_muting')
.select('1') .select('1')
.andWhere(`renote_muting.muterId = ${muterProp}`) .andWhere(`renote_muting.muterId = ${muterProp}`)
.andWhere(`renote_muting.muteeId = ${muteeProp}`); .andWhere(`renote_muting.muteeId = ${muteeProp}`);
return q.andWhere(`NOT EXISTS (${mutingQuery.getQuery()})`, mutingQuery.getParameters()); return q[join](`NOT EXISTS (${mutingQuery.getQuery()})`, mutingQuery.getParameters());
}; };
/** /**
@ -327,13 +346,13 @@ export class QueryService {
* Both props should be expressions, not raw values. * Both props should be expressions, not raw values.
*/ */
@bindThis @bindThis
public excludeMutingInstance<Q extends WhereExpressionBuilder>(q: Q, muterProp: string, muteeProp: string): Q { public excludeMutingInstance<Q extends WhereExpressionBuilder>(q: Q, muterProp: string, muteeProp: string, join: 'andWhere' | 'orWhere' = 'andWhere'): Q {
const mutingInstanceQuery = this.userProfilesRepository.createQueryBuilder('user_profile') const mutingInstanceQuery = this.userProfilesRepository.createQueryBuilder('user_profile')
.select('1') .select('1')
.andWhere(`user_profile.userId = ${muterProp}`) .andWhere(`user_profile.userId = ${muterProp}`)
.andWhere(`"user_profile"."mutedInstances"::jsonb ? ${muteeProp}`); .andWhere(`"user_profile"."mutedInstances"::jsonb ? ${muteeProp}`);
return q.andWhere(`NOT EXISTS (${mutingInstanceQuery.getQuery()})`, mutingInstanceQuery.getParameters()); return q[join](`NOT EXISTS (${mutingInstanceQuery.getQuery()})`, mutingInstanceQuery.getParameters());
} }
/** /**
@ -341,12 +360,12 @@ export class QueryService {
* Both props should be expressions, not raw values. * Both props should be expressions, not raw values.
*/ */
@bindThis @bindThis
public excludeMutingThread<Q extends WhereExpressionBuilder>(q: Q, muterProp: string, muteeProp: string): Q { public excludeMutingThread<Q extends WhereExpressionBuilder>(q: Q, muterProp: string, muteeProp: string, join: 'andWhere' | 'orWhere' = 'andWhere'): Q {
const threadMutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted') const threadMutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted')
.select('1') .select('1')
.andWhere(`threadMuted.userId = ${muterProp}`) .andWhere(`threadMuted.userId = ${muterProp}`)
.andWhere(`threadMuted.threadId = ${muteeProp}`); .andWhere(`threadMuted.threadId = ${muteeProp}`);
return q.andWhere(`NOT EXISTS (${threadMutedQuery.getQuery()})`, threadMutedQuery.getParameters()); return q[join](`NOT EXISTS (${threadMutedQuery.getQuery()})`, threadMutedQuery.getParameters());
} }
} }