From 6742b2021521e00ed083f17892b8954093e5b6bb Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Mon, 4 Nov 2024 09:50:53 +0900 Subject: [PATCH] Fixed sorting by best Comment with pagination (#21506) ref PLG-220 - Added an `orderAttributes` override method to be able to pass `count__likes` to the `findPage` DB helper. - Unknowingly, without that override method in the model, it would strip all 'non-default' queries. - Adding that means we could remove our custom database queries and use the regular `findPage` helper that also handles pagination. --- ghost/core/core/server/models/comment.js | 23 ++----- .../e2e-api/members-comments/comments.test.js | 68 +++++++++++++------ 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/ghost/core/core/server/models/comment.js b/ghost/core/core/server/models/comment.js index 2c09c32a74..5d6bfd6e80 100644 --- a/ghost/core/core/server/models/comment.js +++ b/ghost/core/core/server/models/comment.js @@ -94,6 +94,12 @@ const Comment = ghostBookshelf.Model.extend({ } }, + orderAttributes: function orderAttributes() { + let keys = ghostBookshelf.Model.prototype.orderAttributes.call(this, arguments); + keys.push('count__likes'); + return keys; + }, + onCreated: function onCreated(model, options) { ghostBookshelf.Model.prototype.onCreated.apply(this, arguments); @@ -202,31 +208,14 @@ const Comment = ghostBookshelf.Model.extend({ return options; }, - async findMostLikedComment(options = {}) { - let query = ghostBookshelf.knex('comments') - .select('comments.*') - .count('comment_likes.id as count__likes') // Counting likes for sorting - .leftJoin('comment_likes', 'comments.id', 'comment_likes.comment_id') - .groupBy('comments.id') // Group by comment ID to aggregate likes count - .orderBy('count__likes', 'desc') // Order by likes in descending order (most likes first) - .limit(1); // Limit to just 1 result - // Execute the query and get the result - const result = await query.first(); // Fetch the single top comment - const id = result && result.id; - // Fetch the comment model by ID - return this.findOne({id}, options); - }, - async findPage(options) { const {withRelated} = this.defaultRelations('findPage', options); - const relationsToLoadIndividually = [ 'replies', 'replies.member', 'replies.count.likes', 'replies.count.liked' ].filter(relation => withRelated.includes(relation)); - const result = await ghostBookshelf.Model.findPage.call(this, options); for (const model of result.data) { diff --git a/ghost/core/test/e2e-api/members-comments/comments.test.js b/ghost/core/test/e2e-api/members-comments/comments.test.js index 4c3c54707b..7eeb32e530 100644 --- a/ghost/core/test/e2e-api/members-comments/comments.test.js +++ b/ghost/core/test/e2e-api/members-comments/comments.test.js @@ -510,38 +510,66 @@ describe('Comments API', function () { should.not.exist(response.body.comments[0].unsubscribe_url); }); - it('can show most liked comment first when order param = best', async function () { - await setupBrowseCommentsData(); - const data = await membersAgent - .get(`/api/comments/post/${postId}`); + it('can show most liked comment first when order param = best followed by most recent', async function () { + // await setupBrowseCommentsData(); + // add another comment + await dbFns.addComment({ + html: 'This is the newest comment', + member_id: fixtureManager.get('members', 2).id, + created_at: new Date('2024-08-18') + }); + + const secondBest = await dbFns.addComment({ + member_id: fixtureManager.get('members', 0).id, + html: 'This will be the second best comment', + created_at: new Date('2022-01-01') + }); + + await dbFns.addComment({ + member_id: fixtureManager.get('members', 1).id, + created_at: new Date('2023-01-01') + }); + + const bestComment = await dbFns.addComment({ + member_id: fixtureManager.get('members', 2).id, + html: 'This will be the best comment', + created_at: new Date('2021-01-01') + }); + + const oldestComment = await dbFns.addComment({ + member_id: fixtureManager.get('members', 1).id, + html: 'ancient comment', + created_at: new Date('2019-01-01') + }); await dbFns.addLike({ - comment_id: data.body.comments[1].id, + comment_id: secondBest.id, member_id: loggedInMember.id }); - const data2 = await membersAgent - .get(`/api/comments/post/${postId}/?order=best`) - .expectStatus(200); - - should(data2.body.comments[0].id).eql(data.body.comments[1].id); - }); - - it('does not most liked comment first when order param and keeps normal order', async function () { - await setupBrowseCommentsData(); - const data = await membersAgent - .get(`/api/comments/post/${postId}`); - await dbFns.addLike({ - comment_id: data.body.comments[1].id, + comment_id: bestComment.id, member_id: loggedInMember.id }); + await dbFns.addLike({ + comment_id: bestComment.id, + member_id: fixtureManager.get('members', 0).id + }); + + await dbFns.addLike({ + comment_id: bestComment.id, + member_id: fixtureManager.get('members', 1).id + }); + const data2 = await membersAgent - .get(`/api/comments/post/${postId}/`) + .get(`/api/comments/post/${postId}/?page=1&order=count__likes%20desc%2C%20created_at%20desc`) .expectStatus(200); - should(data2.body.comments[0].id).not.eql(data.body.comments[1].id); + // get the LAST comment from data2 + let lastComment = data2.body.comments[data2.body.comments.length - 1]; + + should(lastComment.id).eql(oldestComment.id); }); it('Can reply to your own comment', async function () {