Optimize again comments list sql query

pull/5544/head
Chocobozzz 2023-01-12 11:11:30 +01:00
parent 3f3530c3db
commit 0b96a0fb77
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
3 changed files with 38 additions and 16 deletions

View File

@ -49,7 +49,7 @@ function getCommentSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]):
if (field === 'totalReplies') {
return [
[ Sequelize.literal('"totalReplies"'), direction ],
[ 'totalReplies', direction ],
lastSort
]
}

View File

@ -44,6 +44,7 @@ export class VideoCommentListQueryBuilder extends AbstractRunQuery {
private innerSelect = ''
private innerJoins = ''
private innerLateralJoins = ''
private innerWhere = ''
private readonly built = {
@ -59,6 +60,10 @@ export class VideoCommentListQueryBuilder extends AbstractRunQuery {
private readonly options: ListVideoCommentsOptions
) {
super(sequelize)
if (this.options.includeReplyCounters && !this.options.videoId) {
throw new Error('Cannot include reply counters without videoId')
}
}
async listComments <T extends Model> () {
@ -97,6 +102,7 @@ export class VideoCommentListQueryBuilder extends AbstractRunQuery {
this.innerQuery = `${this.innerSelect} ` +
`FROM "videoComment" AS "VideoCommentModel" ` +
`${this.innerJoins} ` +
`${this.innerLateralJoins} ` +
`${this.innerWhere} ` +
`${this.getOrder()} ` +
`${this.getInnerLimit()}`
@ -284,11 +290,6 @@ export class VideoCommentListQueryBuilder extends AbstractRunQuery {
toSelect.push(this.tableAttributes.getAvatarAttributes())
}
if (this.options.includeReplyCounters === true) {
toSelect.push(this.getTotalRepliesSelect())
toSelect.push(this.getAuthorTotalRepliesSelect())
}
this.select = this.buildSelect(toSelect)
}
@ -307,6 +308,14 @@ export class VideoCommentListQueryBuilder extends AbstractRunQuery {
])
}
if (this.options.includeReplyCounters === true) {
this.buildTotalRepliesSelect()
this.buildAuthorTotalRepliesSelect()
toSelect.push('"totalRepliesFromVideoAuthor"."count" AS "totalRepliesFromVideoAuthor"')
toSelect.push('"totalReplies"."count" AS "totalReplies"')
}
this.innerSelect = this.buildSelect(toSelect)
}
@ -344,26 +353,32 @@ export class VideoCommentListQueryBuilder extends AbstractRunQuery {
// ---------------------------------------------------------------------------
private getTotalRepliesSelect () {
private buildTotalRepliesSelect () {
const blockWhereString = this.getBlockWhere('replies', 'videoChannel').join(' AND ')
return `(` +
`SELECT COUNT("replies"."id") FROM "videoComment" AS "replies" ` +
`LEFT JOIN "video" ON "video"."id" = "replies"."videoId" ` +
// Help the planner by providing videoId that should filter out many comments
this.replacements.videoId = this.options.videoId
this.innerLateralJoins += `LEFT JOIN LATERAL (` +
`SELECT COUNT("replies"."id") AS "count" FROM "videoComment" AS "replies" ` +
`INNER JOIN "video" ON "video"."id" = "replies"."videoId" AND "replies"."videoId" = :videoId ` +
`LEFT JOIN "videoChannel" ON "video"."channelId" = "videoChannel"."id" ` +
`WHERE "replies"."originCommentId" = "VideoCommentModel"."id" ` +
`AND "deletedAt" IS NULL ` +
`AND ${blockWhereString} ` +
`) AS "totalReplies"`
`) "totalReplies" ON TRUE `
}
private getAuthorTotalRepliesSelect () {
return `(` +
`SELECT COUNT("replies"."id") FROM "videoComment" AS "replies" ` +
`INNER JOIN "video" ON "video"."id" = "replies"."videoId" ` +
private buildAuthorTotalRepliesSelect () {
// Help the planner by providing videoId that should filter out many comments
this.replacements.videoId = this.options.videoId
this.innerLateralJoins += `LEFT JOIN LATERAL (` +
`SELECT COUNT("replies"."id") AS "count" FROM "videoComment" AS "replies" ` +
`INNER JOIN "video" ON "video"."id" = "replies"."videoId" AND "replies"."videoId" = :videoId ` +
`INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ` +
`WHERE "replies"."originCommentId" = "VideoCommentModel"."id" AND "replies"."accountId" = "videoChannel"."accountId"` +
`) AS "totalRepliesFromVideoAuthor"`
`) "totalRepliesFromVideoAuthor" ON TRUE `
}
private getOrder () {

View File

@ -169,6 +169,13 @@ describe('Test video comments', function () {
expect(body.data[2].totalReplies).to.equal(0)
})
it('Should list the and sort them by total replies', async function () {
const body = await command.listThreads({ videoId: videoUUID, sort: 'totalReplies' })
expect(body.data[2].text).to.equal('my super first comment')
expect(body.data[2].totalReplies).to.equal(3)
})
it('Should delete a reply', async function () {
await command.delete({ videoId, commentId: replyToDeleteId })