summaryrefslogtreecommitdiff
path: root/packages/backend/src/core
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2025-06-05 02:34:57 -0400
committerHazelnoot <acomputerdog@gmail.com>2025-06-05 02:34:57 -0400
commit05d7aa0b91525e9029b1e8a638561bf125ca32cb (patch)
treed4010119294e5ebfdd89d4c0b9543541a88d2bdd /packages/backend/src/core
parentfix performance regression in mentions endpoint (diff)
downloadsharkey-05d7aa0b91525e9029b1e8a638561bf125ca32cb.tar.gz
sharkey-05d7aa0b91525e9029b1e8a638561bf125ca32cb.tar.bz2
sharkey-05d7aa0b91525e9029b1e8a638561bf125ca32cb.zip
additional fixes and cleanup to all note endpoints
Diffstat (limited to 'packages/backend/src/core')
-rw-r--r--packages/backend/src/core/LatestNoteService.ts25
-rw-r--r--packages/backend/src/core/QueryService.ts125
2 files changed, 126 insertions, 24 deletions
diff --git a/packages/backend/src/core/LatestNoteService.ts b/packages/backend/src/core/LatestNoteService.ts
index c379805506..63f973c6c6 100644
--- a/packages/backend/src/core/LatestNoteService.ts
+++ b/packages/backend/src/core/LatestNoteService.ts
@@ -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
diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts
index d488505afd..4089fc080c 100644
--- a/packages/backend/src/core/QueryService.ts
+++ b/packages/backend/src/core/QueryService.ts
@@ -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
@@ -257,6 +250,120 @@ export class QueryService {
}
/**
+ * 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.
*/