mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-03-28 22:25:28 +00:00
* migration * update code * tests * triggers * fix * fmt * clippy * post aggregate migration * changes for post aggregate code * wip: update tests for post aggregate * format * fix partialeq * trigger fix * fix post insert trigger * wip * reorder * fixes * community aggregate migration * update code * triggers * person aggregate migration * person aggregate code * person triggers * test fixes * fix scheduled task * update api tests * site_aggregates to local_site migration * site_aggregates code changes * triggers, tests * more fixes * Rename PersonPostAggregates to PostActions * Merge local_user_vote_display_mode into local_user * fix schema * remove duplicate fields * remove "aggregates" from index names * uncomment indices * if count = 0 * remove commentaggregates * Fix triggers in remove aggregates tables pr (#5451) * prevent all db_schema test errors * fix the delete_comments_before_post problem in a way that doesn't affect the returned number of affected rows * remove unnecessary recursion checks and add comment to remaining check * clean up * Fixing SQL format. * Update triggers.sql * Update triggers.sql * Update triggers.sql * Update triggers.sql * remove update of deleted column --------- Co-authored-by: Dessalines <tyhou13@gmx.com> * rename migration * Fix migration errors * Fixing person_saved_combined. (#5481) --------- Co-authored-by: dullbananas <dull.bananas0@gmail.com> Co-authored-by: Dessalines <tyhou13@gmx.com> Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
This commit is contained in:
parent
7b86307760
commit
9a64902ace
65 changed files with 2189 additions and 2397 deletions
api_tests
crates
api/src/local_user
api_common/src
api_crud/src
apub/src
activities/create_or_update
api
collections
db_schema
replaceable_schema
src
db_views/src
combined
inbox_combined_view.rsperson_content_combined_view.rsperson_saved_combined_view.rsreport_combined_view.rssearch_combined_view.rs
comment
community
local_user
person
post
registration_applications
reports
site
structs.rsroutes/src
utils/src
migrations/2025-03-04-105516_remove-aggregate-tables
|
@ -29,7 +29,7 @@
|
||||||
"eslint": "^9.20.0",
|
"eslint": "^9.20.0",
|
||||||
"eslint-plugin-prettier": "^5.2.3",
|
"eslint-plugin-prettier": "^5.2.3",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"lemmy-js-client": "1.0.0-block-nsfw.1",
|
"lemmy-js-client": "0.20.0-remove-aggregate-tables.5",
|
||||||
"prettier": "^3.5.0",
|
"prettier": "^3.5.0",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"tsoa": "^6.6.0",
|
"tsoa": "^6.6.0",
|
||||||
|
|
|
@ -33,8 +33,8 @@ importers:
|
||||||
specifier: ^29.5.0
|
specifier: ^29.5.0
|
||||||
version: 29.7.0(@types/node@22.13.1)
|
version: 29.7.0(@types/node@22.13.1)
|
||||||
lemmy-js-client:
|
lemmy-js-client:
|
||||||
specifier: 1.0.0-block-nsfw.1
|
specifier: 0.20.0-remove-aggregate-tables.5
|
||||||
version: 1.0.0-block-nsfw.1
|
version: 0.20.0-remove-aggregate-tables.5
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.5.0
|
specifier: ^3.5.0
|
||||||
version: 3.5.0
|
version: 3.5.0
|
||||||
|
@ -1528,8 +1528,8 @@ packages:
|
||||||
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
|
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
lemmy-js-client@1.0.0-block-nsfw.1:
|
lemmy-js-client@0.20.0-remove-aggregate-tables.5:
|
||||||
resolution: {integrity: sha512-7dIGSflkfl6JZ57tNNwoI4xwHc3uMkj9mp3lMMUh+DkSnvUNEc1BCk4sBGLYJTXGr4XreeJT99bM67RO8fGkmA==}
|
resolution: {integrity: sha512-A/p4LLWNiVp7fsQOctbFm/biBAunk0FIl5X79WJ/hRu/UiD1M+tCLGYPGdy308R+zscZsDNqECe1LYBenOFzOA==}
|
||||||
|
|
||||||
leven@3.1.0:
|
leven@3.1.0:
|
||||||
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
|
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
|
||||||
|
@ -4169,7 +4169,7 @@ snapshots:
|
||||||
|
|
||||||
kleur@3.0.3: {}
|
kleur@3.0.3: {}
|
||||||
|
|
||||||
lemmy-js-client@1.0.0-block-nsfw.1: {}
|
lemmy-js-client@0.20.0-remove-aggregate-tables.5: {}
|
||||||
|
|
||||||
leven@3.1.0: {}
|
leven@3.1.0: {}
|
||||||
|
|
||||||
|
|
|
@ -81,19 +81,19 @@ test("Create a comment", async () => {
|
||||||
expect(commentRes.comment_view.comment.content).toBeDefined();
|
expect(commentRes.comment_view.comment.content).toBeDefined();
|
||||||
expect(commentRes.comment_view.community.local).toBe(false);
|
expect(commentRes.comment_view.community.local).toBe(false);
|
||||||
expect(commentRes.comment_view.creator.local).toBe(true);
|
expect(commentRes.comment_view.creator.local).toBe(true);
|
||||||
expect(commentRes.comment_view.counts.score).toBe(1);
|
expect(commentRes.comment_view.comment.score).toBe(1);
|
||||||
|
|
||||||
// Make sure that comment is liked on beta
|
// Make sure that comment is liked on beta
|
||||||
let betaComment = (
|
let betaComment = (
|
||||||
await waitUntil(
|
await waitUntil(
|
||||||
() => resolveComment(beta, commentRes.comment_view.comment),
|
() => resolveComment(beta, commentRes.comment_view.comment),
|
||||||
c => c.comment?.counts.score === 1,
|
c => c.comment?.comment.score === 1,
|
||||||
)
|
)
|
||||||
).comment;
|
).comment;
|
||||||
expect(betaComment).toBeDefined();
|
expect(betaComment).toBeDefined();
|
||||||
expect(betaComment?.community.local).toBe(true);
|
expect(betaComment?.community.local).toBe(true);
|
||||||
expect(betaComment?.creator.local).toBe(false);
|
expect(betaComment?.creator.local).toBe(false);
|
||||||
expect(betaComment?.counts.score).toBe(1);
|
expect(betaComment?.comment.score).toBe(1);
|
||||||
assertCommentFederation(betaComment, commentRes.comment_view);
|
assertCommentFederation(betaComment, commentRes.comment_view);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -293,48 +293,48 @@ test("Unlike a comment", async () => {
|
||||||
let gammaComment1 = (
|
let gammaComment1 = (
|
||||||
await waitUntil(
|
await waitUntil(
|
||||||
() => resolveComment(gamma, commentRes.comment_view.comment),
|
() => resolveComment(gamma, commentRes.comment_view.comment),
|
||||||
c => c.comment?.counts.score === 1,
|
c => c.comment?.comment.score === 1,
|
||||||
)
|
)
|
||||||
).comment;
|
).comment;
|
||||||
expect(gammaComment1).toBeDefined();
|
expect(gammaComment1).toBeDefined();
|
||||||
expect(gammaComment1?.community.local).toBe(false);
|
expect(gammaComment1?.community.local).toBe(false);
|
||||||
expect(gammaComment1?.creator.local).toBe(false);
|
expect(gammaComment1?.creator.local).toBe(false);
|
||||||
expect(gammaComment1?.counts.score).toBe(1);
|
expect(gammaComment1?.comment.score).toBe(1);
|
||||||
|
|
||||||
let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment);
|
let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment);
|
||||||
expect(unlike.comment_view.counts.score).toBe(0);
|
expect(unlike.comment_view.comment.score).toBe(0);
|
||||||
|
|
||||||
// Make sure that comment is unliked on beta
|
// Make sure that comment is unliked on beta
|
||||||
let betaComment = (
|
let betaComment = (
|
||||||
await waitUntil(
|
await waitUntil(
|
||||||
() => resolveComment(beta, commentRes.comment_view.comment),
|
() => resolveComment(beta, commentRes.comment_view.comment),
|
||||||
c => c.comment?.counts.score === 0,
|
c => c.comment?.comment.score === 0,
|
||||||
)
|
)
|
||||||
).comment;
|
).comment;
|
||||||
expect(betaComment).toBeDefined();
|
expect(betaComment).toBeDefined();
|
||||||
expect(betaComment?.community.local).toBe(true);
|
expect(betaComment?.community.local).toBe(true);
|
||||||
expect(betaComment?.creator.local).toBe(false);
|
expect(betaComment?.creator.local).toBe(false);
|
||||||
expect(betaComment?.counts.score).toBe(0);
|
expect(betaComment?.comment.score).toBe(0);
|
||||||
|
|
||||||
// Make sure that comment is unliked on gamma, downstream peer
|
// Make sure that comment is unliked on gamma, downstream peer
|
||||||
// This is testing replication from remote-home-remote (alpha-beta-gamma)
|
// This is testing replication from remote-home-remote (alpha-beta-gamma)
|
||||||
let gammaComment = (
|
let gammaComment = (
|
||||||
await waitUntil(
|
await waitUntil(
|
||||||
() => resolveComment(gamma, commentRes.comment_view.comment),
|
() => resolveComment(gamma, commentRes.comment_view.comment),
|
||||||
c => c.comment?.counts.score === 0,
|
c => c.comment?.comment.score === 0,
|
||||||
)
|
)
|
||||||
).comment;
|
).comment;
|
||||||
expect(gammaComment).toBeDefined();
|
expect(gammaComment).toBeDefined();
|
||||||
expect(gammaComment?.community.local).toBe(false);
|
expect(gammaComment?.community.local).toBe(false);
|
||||||
expect(gammaComment?.creator.local).toBe(false);
|
expect(gammaComment?.creator.local).toBe(false);
|
||||||
expect(gammaComment?.counts.score).toBe(0);
|
expect(gammaComment?.comment.score).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Federated comment like", async () => {
|
test("Federated comment like", async () => {
|
||||||
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
await waitUntil(
|
await waitUntil(
|
||||||
() => resolveComment(beta, commentRes.comment_view.comment),
|
() => resolveComment(beta, commentRes.comment_view.comment),
|
||||||
c => c.comment?.counts.score === 1,
|
c => c.comment?.comment.score === 1,
|
||||||
);
|
);
|
||||||
// Find the comment on beta
|
// Find the comment on beta
|
||||||
let betaComment = (
|
let betaComment = (
|
||||||
|
@ -346,14 +346,14 @@ test("Federated comment like", async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let like = await likeComment(beta, 1, betaComment.comment);
|
let like = await likeComment(beta, 1, betaComment.comment);
|
||||||
expect(like.comment_view.counts.score).toBe(2);
|
expect(like.comment_view.comment.score).toBe(2);
|
||||||
|
|
||||||
// Get the post from alpha, check the likes
|
// Get the post from alpha, check the likes
|
||||||
let postComments = await waitUntil(
|
let postComments = await waitUntil(
|
||||||
() => getComments(alpha, postOnAlphaRes.post_view.post.id),
|
() => getComments(alpha, postOnAlphaRes.post_view.post.id),
|
||||||
c => c.comments[0].counts.score === 2,
|
c => c.comments[0].comment.score === 2,
|
||||||
);
|
);
|
||||||
expect(postComments.comments[0].counts.score).toBe(2);
|
expect(postComments.comments[0].comment.score).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Reply to a comment from another instance, get notification", async () => {
|
test("Reply to a comment from another instance, get notification", async () => {
|
||||||
|
@ -377,7 +377,7 @@ test("Reply to a comment from another instance, get notification", async () => {
|
||||||
let betaComment = (
|
let betaComment = (
|
||||||
await waitUntil(
|
await waitUntil(
|
||||||
() => resolveComment(beta, commentRes.comment_view.comment),
|
() => resolveComment(beta, commentRes.comment_view.comment),
|
||||||
c => c.comment?.counts.score === 1,
|
c => c.comment?.comment.score === 1,
|
||||||
)
|
)
|
||||||
).comment;
|
).comment;
|
||||||
|
|
||||||
|
@ -397,12 +397,12 @@ test("Reply to a comment from another instance, get notification", async () => {
|
||||||
expect(getCommentParentId(replyRes.comment_view.comment)).toBe(
|
expect(getCommentParentId(replyRes.comment_view.comment)).toBe(
|
||||||
betaComment.comment.id,
|
betaComment.comment.id,
|
||||||
);
|
);
|
||||||
expect(replyRes.comment_view.counts.score).toBe(1);
|
expect(replyRes.comment_view.comment.score).toBe(1);
|
||||||
|
|
||||||
// Make sure that reply comment is seen on alpha
|
// Make sure that reply comment is seen on alpha
|
||||||
let commentSearch = await waitUntil(
|
let commentSearch = await waitUntil(
|
||||||
() => resolveComment(alpha, replyRes.comment_view.comment),
|
() => resolveComment(alpha, replyRes.comment_view.comment),
|
||||||
c => c.comment?.counts.score === 1,
|
c => c.comment?.comment.score === 1,
|
||||||
);
|
);
|
||||||
let alphaComment = commentSearch.comment!;
|
let alphaComment = commentSearch.comment!;
|
||||||
let postComments = await waitUntil(
|
let postComments = await waitUntil(
|
||||||
|
@ -418,7 +418,7 @@ test("Reply to a comment from another instance, get notification", async () => {
|
||||||
);
|
);
|
||||||
expect(alphaComment.community.local).toBe(false);
|
expect(alphaComment.community.local).toBe(false);
|
||||||
expect(alphaComment.creator.local).toBe(false);
|
expect(alphaComment.creator.local).toBe(false);
|
||||||
expect(alphaComment.counts.score).toBe(1);
|
expect(alphaComment.comment.score).toBe(1);
|
||||||
assertCommentFederation(alphaComment, replyRes.comment_view);
|
assertCommentFederation(alphaComment, replyRes.comment_view);
|
||||||
|
|
||||||
// Did alpha get notified of the reply from beta?
|
// Did alpha get notified of the reply from beta?
|
||||||
|
@ -441,7 +441,7 @@ test("Reply to a comment from another instance, get notification", async () => {
|
||||||
expect(alphaReply.comment.content).toBeDefined();
|
expect(alphaReply.comment.content).toBeDefined();
|
||||||
expect(alphaReply.community.local).toBe(false);
|
expect(alphaReply.community.local).toBe(false);
|
||||||
expect(alphaReply.creator.local).toBe(false);
|
expect(alphaReply.creator.local).toBe(false);
|
||||||
expect(alphaReply.counts.score).toBe(1);
|
expect(alphaReply.comment.score).toBe(1);
|
||||||
// ToDo: interesting alphaRepliesRes.replies[0].comment_reply.id is 1, meaning? how did that come about?
|
// ToDo: interesting alphaRepliesRes.replies[0].comment_reply.id is 1, meaning? how did that come about?
|
||||||
expect(alphaReply.comment.id).toBe(alphaComment.comment.id);
|
expect(alphaReply.comment.id).toBe(alphaComment.comment.id);
|
||||||
// this is a new notification, getReplies fetch was for read/unread both, confirm it is unread.
|
// this is a new notification, getReplies fetch was for read/unread both, confirm it is unread.
|
||||||
|
@ -515,7 +515,7 @@ test("Mention beta from alpha comment", async () => {
|
||||||
expect(mentionRes.comment_view.comment.content).toBeDefined();
|
expect(mentionRes.comment_view.comment.content).toBeDefined();
|
||||||
expect(mentionRes.comment_view.community.local).toBe(false);
|
expect(mentionRes.comment_view.community.local).toBe(false);
|
||||||
expect(mentionRes.comment_view.creator.local).toBe(true);
|
expect(mentionRes.comment_view.creator.local).toBe(true);
|
||||||
expect(mentionRes.comment_view.counts.score).toBe(1);
|
expect(mentionRes.comment_view.comment.score).toBe(1);
|
||||||
|
|
||||||
// get beta's localized copy of the alpha post
|
// get beta's localized copy of the alpha post
|
||||||
let betaPost = await waitForPost(beta, postOnAlphaRes.post_view.post);
|
let betaPost = await waitForPost(beta, postOnAlphaRes.post_view.post);
|
||||||
|
@ -528,7 +528,7 @@ test("Mention beta from alpha comment", async () => {
|
||||||
// Make sure that both new comments are seen on beta and have parent/child relationship
|
// Make sure that both new comments are seen on beta and have parent/child relationship
|
||||||
let betaPostComments = await waitUntil(
|
let betaPostComments = await waitUntil(
|
||||||
() => getComments(beta, betaPost!.post.id),
|
() => getComments(beta, betaPost!.post.id),
|
||||||
c => c.comments[1]?.counts.score === 1,
|
c => c.comments[1]?.comment.score === 1,
|
||||||
);
|
);
|
||||||
expect(betaPostComments.comments.length).toEqual(2);
|
expect(betaPostComments.comments.length).toEqual(2);
|
||||||
// the trunk-branch root comment will be older than the mention reply comment, so index 1
|
// the trunk-branch root comment will be older than the mention reply comment, so index 1
|
||||||
|
@ -542,7 +542,7 @@ test("Mention beta from alpha comment", async () => {
|
||||||
);
|
);
|
||||||
expect(betaRootComment.community.local).toBe(true);
|
expect(betaRootComment.community.local).toBe(true);
|
||||||
expect(betaRootComment.creator.local).toBe(false);
|
expect(betaRootComment.creator.local).toBe(false);
|
||||||
expect(betaRootComment.counts.score).toBe(1);
|
expect(betaRootComment.comment.score).toBe(1);
|
||||||
assertCommentFederation(betaRootComment, commentRes.comment_view);
|
assertCommentFederation(betaRootComment, commentRes.comment_view);
|
||||||
|
|
||||||
let mentionsRes = await waitUntil(
|
let mentionsRes = await waitUntil(
|
||||||
|
@ -554,7 +554,7 @@ test("Mention beta from alpha comment", async () => {
|
||||||
expect(firstMention.comment.content).toBeDefined();
|
expect(firstMention.comment.content).toBeDefined();
|
||||||
expect(firstMention.community.local).toBe(true);
|
expect(firstMention.community.local).toBe(true);
|
||||||
expect(firstMention.creator.local).toBe(false);
|
expect(firstMention.creator.local).toBe(false);
|
||||||
expect(firstMention.counts.score).toBe(1);
|
expect(firstMention.comment.score).toBe(1);
|
||||||
// the reply comment with mention should be the most fresh, newest, index 0
|
// the reply comment with mention should be the most fresh, newest, index 0
|
||||||
expect(firstMention.person_comment_mention.comment_id).toBe(
|
expect(firstMention.person_comment_mention.comment_id).toBe(
|
||||||
betaPostComments.comments[0].comment.id,
|
betaPostComments.comments[0].comment.id,
|
||||||
|
@ -606,17 +606,17 @@ test("A and G subscribe to B (center) A posts, G mentions B, it gets announced t
|
||||||
expect(commentRes.comment_view.comment.content).toBe(commentContent);
|
expect(commentRes.comment_view.comment.content).toBe(commentContent);
|
||||||
expect(commentRes.comment_view.community.local).toBe(false);
|
expect(commentRes.comment_view.community.local).toBe(false);
|
||||||
expect(commentRes.comment_view.creator.local).toBe(true);
|
expect(commentRes.comment_view.creator.local).toBe(true);
|
||||||
expect(commentRes.comment_view.counts.score).toBe(1);
|
expect(commentRes.comment_view.comment.score).toBe(1);
|
||||||
|
|
||||||
// Make sure alpha sees it
|
// Make sure alpha sees it
|
||||||
let alphaPostComments2 = await waitUntil(
|
let alphaPostComments2 = await waitUntil(
|
||||||
() => getComments(alpha, alphaPost.post_view.post.id),
|
() => getComments(alpha, alphaPost.post_view.post.id),
|
||||||
e => e.comments[0]?.counts.score === 1,
|
e => e.comments[0]?.comment.score === 1,
|
||||||
);
|
);
|
||||||
expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent);
|
expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent);
|
||||||
expect(alphaPostComments2.comments[0].community.local).toBe(true);
|
expect(alphaPostComments2.comments[0].community.local).toBe(true);
|
||||||
expect(alphaPostComments2.comments[0].creator.local).toBe(false);
|
expect(alphaPostComments2.comments[0].creator.local).toBe(false);
|
||||||
expect(alphaPostComments2.comments[0].counts.score).toBe(1);
|
expect(alphaPostComments2.comments[0].comment.score).toBe(1);
|
||||||
assertCommentFederation(
|
assertCommentFederation(
|
||||||
alphaPostComments2.comments[0],
|
alphaPostComments2.comments[0],
|
||||||
commentRes.comment_view,
|
commentRes.comment_view,
|
||||||
|
@ -688,17 +688,17 @@ test("Check that activity from another instance is sent to third instance", asyn
|
||||||
expect(commentRes.comment_view.comment.content).toBe(commentContent);
|
expect(commentRes.comment_view.comment.content).toBe(commentContent);
|
||||||
expect(commentRes.comment_view.community.local).toBe(false);
|
expect(commentRes.comment_view.community.local).toBe(false);
|
||||||
expect(commentRes.comment_view.creator.local).toBe(true);
|
expect(commentRes.comment_view.creator.local).toBe(true);
|
||||||
expect(commentRes.comment_view.counts.score).toBe(1);
|
expect(commentRes.comment_view.comment.score).toBe(1);
|
||||||
|
|
||||||
// Make sure alpha sees it
|
// Make sure alpha sees it
|
||||||
let alphaPostComments2 = await waitUntil(
|
let alphaPostComments2 = await waitUntil(
|
||||||
() => getComments(alpha, alphaPost!.post.id),
|
() => getComments(alpha, alphaPost!.post.id),
|
||||||
e => e.comments[0]?.counts.score === 1,
|
e => e.comments[0]?.comment.score === 1,
|
||||||
);
|
);
|
||||||
expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent);
|
expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent);
|
||||||
expect(alphaPostComments2.comments[0].community.local).toBe(false);
|
expect(alphaPostComments2.comments[0].community.local).toBe(false);
|
||||||
expect(alphaPostComments2.comments[0].creator.local).toBe(false);
|
expect(alphaPostComments2.comments[0].creator.local).toBe(false);
|
||||||
expect(alphaPostComments2.comments[0].counts.score).toBe(1);
|
expect(alphaPostComments2.comments[0].comment.score).toBe(1);
|
||||||
assertCommentFederation(
|
assertCommentFederation(
|
||||||
alphaPostComments2.comments[0],
|
alphaPostComments2.comments[0],
|
||||||
commentRes.comment_view,
|
commentRes.comment_view,
|
||||||
|
|
|
@ -396,7 +396,7 @@ test.skip("Community follower count is federated", async () => {
|
||||||
).community;
|
).community;
|
||||||
|
|
||||||
// Make sure there is 1 subscriber
|
// Make sure there is 1 subscriber
|
||||||
expect(followed?.counts.subscribers).toBe(1);
|
expect(followed?.community.subscribers).toBe(1);
|
||||||
|
|
||||||
// Follow the community from gamma
|
// Follow the community from gamma
|
||||||
resolved = await resolveCommunity(gamma, communityActorId);
|
resolved = await resolveCommunity(gamma, communityActorId);
|
||||||
|
@ -413,7 +413,7 @@ test.skip("Community follower count is federated", async () => {
|
||||||
).community;
|
).community;
|
||||||
|
|
||||||
// Make sure there are 2 subscribers
|
// Make sure there are 2 subscribers
|
||||||
expect(followed?.counts?.subscribers).toBe(2);
|
expect(followed?.community?.subscribers).toBe(2);
|
||||||
|
|
||||||
// Follow the community from delta
|
// Follow the community from delta
|
||||||
resolved = await resolveCommunity(delta, communityActorId);
|
resolved = await resolveCommunity(delta, communityActorId);
|
||||||
|
@ -430,13 +430,13 @@ test.skip("Community follower count is federated", async () => {
|
||||||
).community;
|
).community;
|
||||||
|
|
||||||
// Make sure there are 3 subscribers
|
// Make sure there are 3 subscribers
|
||||||
expect(followed?.counts?.subscribers).toBe(3);
|
expect(followed?.community?.subscribers).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Dont receive community activities after unsubscribe", async () => {
|
test("Dont receive community activities after unsubscribe", async () => {
|
||||||
let communityRes = await createCommunity(alpha);
|
let communityRes = await createCommunity(alpha);
|
||||||
expect(communityRes.community_view.community.name).toBeDefined();
|
expect(communityRes.community_view.community.name).toBeDefined();
|
||||||
expect(communityRes.community_view.counts.subscribers).toBe(1);
|
expect(communityRes.community_view.community.subscribers).toBe(1);
|
||||||
|
|
||||||
let betaCommunity = (
|
let betaCommunity = (
|
||||||
await resolveCommunity(beta, communityRes.community_view.community.ap_id)
|
await resolveCommunity(beta, communityRes.community_view.community.ap_id)
|
||||||
|
@ -451,7 +451,7 @@ test("Dont receive community activities after unsubscribe", async () => {
|
||||||
alpha,
|
alpha,
|
||||||
communityRes.community_view.community.id,
|
communityRes.community_view.community.id,
|
||||||
);
|
);
|
||||||
expect(communityRes1.community_view.counts.subscribers).toBe(2);
|
expect(communityRes1.community_view.community.subscribers).toBe(2);
|
||||||
|
|
||||||
// temporarily block alpha, so that it doesn't know about unfollow
|
// temporarily block alpha, so that it doesn't know about unfollow
|
||||||
var allow_instance_params: AdminAllowInstanceParams = {
|
var allow_instance_params: AdminAllowInstanceParams = {
|
||||||
|
@ -470,7 +470,7 @@ test("Dont receive community activities after unsubscribe", async () => {
|
||||||
alpha,
|
alpha,
|
||||||
communityRes.community_view.community.id,
|
communityRes.community_view.community.id,
|
||||||
);
|
);
|
||||||
expect(communityRes2.community_view.counts.subscribers).toBe(2);
|
expect(communityRes2.community_view.community.subscribers).toBe(2);
|
||||||
|
|
||||||
// unblock alpha
|
// unblock alpha
|
||||||
allow_instance_params.allow = true;
|
allow_instance_params.allow = true;
|
||||||
|
@ -492,7 +492,7 @@ test("Dont receive community activities after unsubscribe", async () => {
|
||||||
test("Fetch community, includes posts", async () => {
|
test("Fetch community, includes posts", async () => {
|
||||||
let communityRes = await createCommunity(alpha);
|
let communityRes = await createCommunity(alpha);
|
||||||
expect(communityRes.community_view.community.name).toBeDefined();
|
expect(communityRes.community_view.community.name).toBeDefined();
|
||||||
expect(communityRes.community_view.counts.subscribers).toBe(1);
|
expect(communityRes.community_view.community.subscribers).toBe(1);
|
||||||
|
|
||||||
let postRes = await createPost(
|
let postRes = await createPost(
|
||||||
alpha,
|
alpha,
|
||||||
|
|
|
@ -27,21 +27,21 @@ test("Follow local community", async () => {
|
||||||
// Make sure the follow response went through
|
// Make sure the follow response went through
|
||||||
expect(follow.community_view.community.local).toBe(true);
|
expect(follow.community_view.community.local).toBe(true);
|
||||||
expect(follow.community_view.subscribed).toBe("Subscribed");
|
expect(follow.community_view.subscribed).toBe("Subscribed");
|
||||||
expect(follow.community_view.counts.subscribers).toBe(
|
expect(follow.community_view.community.subscribers).toBe(
|
||||||
community.counts.subscribers + 1,
|
community.community.subscribers + 1,
|
||||||
);
|
);
|
||||||
expect(follow.community_view.counts.subscribers_local).toBe(
|
expect(follow.community_view.community.subscribers_local).toBe(
|
||||||
community.counts.subscribers_local + 1,
|
community.community.subscribers_local + 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test an unfollow
|
// Test an unfollow
|
||||||
let unfollow = await followCommunity(user, false, community.community.id);
|
let unfollow = await followCommunity(user, false, community.community.id);
|
||||||
expect(unfollow.community_view.subscribed).toBe("NotSubscribed");
|
expect(unfollow.community_view.subscribed).toBe("NotSubscribed");
|
||||||
expect(unfollow.community_view.counts.subscribers).toBe(
|
expect(unfollow.community_view.community.subscribers).toBe(
|
||||||
community.counts.subscribers,
|
community.community.subscribers,
|
||||||
);
|
);
|
||||||
expect(unfollow.community_view.counts.subscribers_local).toBe(
|
expect(unfollow.community_view.community.subscribers_local).toBe(
|
||||||
community.counts.subscribers_local,
|
community.community.subscribers_local,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ test("Follow federated community", async () => {
|
||||||
const betaCommunityInitial = (
|
const betaCommunityInitial = (
|
||||||
await waitUntil(
|
await waitUntil(
|
||||||
() => resolveBetaCommunity(alpha),
|
() => resolveBetaCommunity(alpha),
|
||||||
c => !!c.community && c.community?.counts.subscribers >= 1,
|
c => !!c.community && c.community?.community.subscribers >= 1,
|
||||||
)
|
)
|
||||||
).community;
|
).community;
|
||||||
if (!betaCommunityInitial) {
|
if (!betaCommunityInitial) {
|
||||||
|
@ -74,14 +74,14 @@ test("Follow federated community", async () => {
|
||||||
expect(betaCommunity?.community.local).toBe(false);
|
expect(betaCommunity?.community.local).toBe(false);
|
||||||
expect(betaCommunity?.community.name).toBe("main");
|
expect(betaCommunity?.community.name).toBe("main");
|
||||||
expect(betaCommunity?.subscribed).toBe("Subscribed");
|
expect(betaCommunity?.subscribed).toBe("Subscribed");
|
||||||
expect(betaCommunity?.counts.subscribers_local).toBe(
|
expect(betaCommunity?.community.subscribers_local).toBe(
|
||||||
betaCommunityInitial.counts.subscribers_local + 1,
|
betaCommunityInitial.community.subscribers_local + 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
// check that unfollow was federated
|
// check that unfollow was federated
|
||||||
let communityOnBeta1 = await resolveBetaCommunity(beta);
|
let communityOnBeta1 = await resolveBetaCommunity(beta);
|
||||||
expect(communityOnBeta1.community?.counts.subscribers).toBe(
|
expect(communityOnBeta1.community?.community.subscribers).toBe(
|
||||||
betaCommunityInitial.counts.subscribers + 1,
|
betaCommunityInitial.community.subscribers + 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check it from local
|
// Check it from local
|
||||||
|
@ -113,11 +113,11 @@ test("Follow federated community", async () => {
|
||||||
let communityOnBeta2 = await waitUntil(
|
let communityOnBeta2 = await waitUntil(
|
||||||
() => resolveBetaCommunity(beta),
|
() => resolveBetaCommunity(beta),
|
||||||
c =>
|
c =>
|
||||||
c.community?.counts.subscribers ===
|
c.community?.community.subscribers ===
|
||||||
betaCommunityInitial.counts.subscribers,
|
betaCommunityInitial.community.subscribers,
|
||||||
);
|
);
|
||||||
expect(communityOnBeta2.community?.counts.subscribers).toBe(
|
expect(communityOnBeta2.community?.community.subscribers).toBe(
|
||||||
betaCommunityInitial.counts.subscribers,
|
betaCommunityInitial.community.subscribers,
|
||||||
);
|
);
|
||||||
expect(communityOnBeta2.community?.counts.subscribers_local).toBe(1);
|
expect(communityOnBeta2.community?.community.subscribers_local).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -125,19 +125,19 @@ test("Create a post", async () => {
|
||||||
expect(postRes.post_view.post).toBeDefined();
|
expect(postRes.post_view.post).toBeDefined();
|
||||||
expect(postRes.post_view.community.local).toBe(false);
|
expect(postRes.post_view.community.local).toBe(false);
|
||||||
expect(postRes.post_view.creator.local).toBe(true);
|
expect(postRes.post_view.creator.local).toBe(true);
|
||||||
expect(postRes.post_view.counts.score).toBe(1);
|
expect(postRes.post_view.post.score).toBe(1);
|
||||||
|
|
||||||
// Make sure that post is liked on beta
|
// Make sure that post is liked on beta
|
||||||
const betaPost = await waitForPost(
|
const betaPost = await waitForPost(
|
||||||
beta,
|
beta,
|
||||||
postRes.post_view.post,
|
postRes.post_view.post,
|
||||||
res => res?.counts.score === 1,
|
res => res?.post.score === 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(betaPost).toBeDefined();
|
expect(betaPost).toBeDefined();
|
||||||
expect(betaPost?.community.local).toBe(true);
|
expect(betaPost?.community.local).toBe(true);
|
||||||
expect(betaPost?.creator.local).toBe(false);
|
expect(betaPost?.creator.local).toBe(false);
|
||||||
expect(betaPost?.counts.score).toBe(1);
|
expect(betaPost?.post.score).toBe(1);
|
||||||
await assertPostFederation(betaPost, postRes.post_view);
|
await assertPostFederation(betaPost, postRes.post_view);
|
||||||
|
|
||||||
// Delta only follows beta, so it should not see an alpha ap_id
|
// Delta only follows beta, so it should not see an alpha ap_id
|
||||||
|
@ -165,23 +165,23 @@ test("Unlike a post", async () => {
|
||||||
}
|
}
|
||||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||||
let unlike = await likePost(alpha, 0, postRes.post_view.post);
|
let unlike = await likePost(alpha, 0, postRes.post_view.post);
|
||||||
expect(unlike.post_view.counts.score).toBe(0);
|
expect(unlike.post_view.post.score).toBe(0);
|
||||||
|
|
||||||
// Try to unlike it again, make sure it stays at 0
|
// Try to unlike it again, make sure it stays at 0
|
||||||
let unlike2 = await likePost(alpha, 0, postRes.post_view.post);
|
let unlike2 = await likePost(alpha, 0, postRes.post_view.post);
|
||||||
expect(unlike2.post_view.counts.score).toBe(0);
|
expect(unlike2.post_view.post.score).toBe(0);
|
||||||
|
|
||||||
// Make sure that post is unliked on beta
|
// Make sure that post is unliked on beta
|
||||||
const betaPost = await waitForPost(
|
const betaPost = await waitForPost(
|
||||||
beta,
|
beta,
|
||||||
postRes.post_view.post,
|
postRes.post_view.post,
|
||||||
post => post?.counts.score === 0,
|
post => post?.post.score === 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(betaPost).toBeDefined();
|
expect(betaPost).toBeDefined();
|
||||||
expect(betaPost?.community.local).toBe(true);
|
expect(betaPost?.community.local).toBe(true);
|
||||||
expect(betaPost?.creator.local).toBe(false);
|
expect(betaPost?.creator.local).toBe(false);
|
||||||
expect(betaPost?.counts.score).toBe(0);
|
expect(betaPost?.post.score).toBe(0);
|
||||||
await assertPostFederation(betaPost, postRes.post_view);
|
await assertPostFederation(betaPost, postRes.post_view);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -665,7 +665,7 @@ test("Enforce community ban for federated user", async () => {
|
||||||
expect(postRes3.post_view.post).toBeDefined();
|
expect(postRes3.post_view.post).toBeDefined();
|
||||||
expect(postRes3.post_view.community.local).toBe(false);
|
expect(postRes3.post_view.community.local).toBe(false);
|
||||||
expect(postRes3.post_view.creator.local).toBe(true);
|
expect(postRes3.post_view.creator.local).toBe(true);
|
||||||
expect(postRes3.post_view.counts.score).toBe(1);
|
expect(postRes3.post_view.post.score).toBe(1);
|
||||||
|
|
||||||
// Make sure that post makes it to beta community
|
// Make sure that post makes it to beta community
|
||||||
let postRes4 = await waitForPost(beta, postRes3.post_view.post);
|
let postRes4 = await waitForPost(beta, postRes3.post_view.post);
|
||||||
|
@ -806,7 +806,7 @@ test("Fetch post via redirect", async () => {
|
||||||
const betaPost = await waitForPost(
|
const betaPost = await waitForPost(
|
||||||
beta,
|
beta,
|
||||||
alphaPost.post_view.post,
|
alphaPost.post_view.post,
|
||||||
res => res?.counts.score === 1,
|
res => res?.post.score === 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(betaPost).toBeDefined();
|
expect(betaPost).toBeDefined();
|
||||||
|
@ -879,7 +879,7 @@ test("Mention beta from alpha post body", async () => {
|
||||||
expect(postOnAlphaRes.post_view.post.body).toBeDefined();
|
expect(postOnAlphaRes.post_view.post.body).toBeDefined();
|
||||||
expect(postOnAlphaRes.post_view.community.local).toBe(false);
|
expect(postOnAlphaRes.post_view.community.local).toBe(false);
|
||||||
expect(postOnAlphaRes.post_view.creator.local).toBe(true);
|
expect(postOnAlphaRes.post_view.creator.local).toBe(true);
|
||||||
expect(postOnAlphaRes.post_view.counts.score).toBe(1);
|
expect(postOnAlphaRes.post_view.post.score).toBe(1);
|
||||||
|
|
||||||
// get beta's localized copy of the alpha post
|
// get beta's localized copy of the alpha post
|
||||||
let betaPost = await waitForPost(beta, postOnAlphaRes.post_view.post);
|
let betaPost = await waitForPost(beta, postOnAlphaRes.post_view.post);
|
||||||
|
@ -899,7 +899,7 @@ test("Mention beta from alpha post body", async () => {
|
||||||
expect(firstMention.post.body).toBeDefined();
|
expect(firstMention.post.body).toBeDefined();
|
||||||
expect(firstMention.community.local).toBe(true);
|
expect(firstMention.community.local).toBe(true);
|
||||||
expect(firstMention.creator.local).toBe(false);
|
expect(firstMention.creator.local).toBe(false);
|
||||||
expect(firstMention.counts.score).toBe(1);
|
expect(firstMention.post.score).toBe(1);
|
||||||
expect(firstMention.person_post_mention.post_id).toBe(betaPost.post.id);
|
expect(firstMention.person_post_mention.post_id).toBe(betaPost.post.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
actor_language::LocalUserLanguage,
|
actor_language::LocalUserLanguage,
|
||||||
local_user::{LocalUser, LocalUserUpdateForm},
|
local_user::{LocalUser, LocalUserUpdateForm},
|
||||||
local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeUpdateForm},
|
|
||||||
person::{Person, PersonUpdateForm},
|
person::{Person, PersonUpdateForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
|
@ -132,20 +131,15 @@ pub async fn save_user_settings(
|
||||||
collapse_bot_comments: data.collapse_bot_comments,
|
collapse_bot_comments: data.collapse_bot_comments,
|
||||||
auto_mark_fetched_posts_as_read: data.auto_mark_fetched_posts_as_read,
|
auto_mark_fetched_posts_as_read: data.auto_mark_fetched_posts_as_read,
|
||||||
hide_media: data.hide_media,
|
hide_media: data.hide_media,
|
||||||
|
// Update the vote display modes
|
||||||
|
show_score: data.show_scores,
|
||||||
|
show_upvotes: data.show_upvotes,
|
||||||
|
show_downvotes: data.show_downvotes,
|
||||||
|
show_upvote_percentage: data.show_upvote_percentage,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
LocalUser::update(&mut context.pool(), local_user_id, &local_user_form).await?;
|
LocalUser::update(&mut context.pool(), local_user_id, &local_user_form).await?;
|
||||||
|
|
||||||
// Update the vote display modes
|
|
||||||
let vote_display_modes_form = LocalUserVoteDisplayModeUpdateForm {
|
|
||||||
score: data.show_scores,
|
|
||||||
upvotes: data.show_upvotes,
|
|
||||||
downvotes: data.show_downvotes,
|
|
||||||
upvote_percentage: data.show_upvote_percentage,
|
|
||||||
};
|
|
||||||
LocalUserVoteDisplayMode::update(&mut context.pool(), local_user_id, &vote_display_modes_form)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Json(SuccessResponse::default()))
|
Ok(Json(SuccessResponse::default()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
|
||||||
use chrono::{DateTime, Days, Local, TimeZone, Utc};
|
use chrono::{DateTime, Days, Local, TimeZone, Utc};
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
|
|
||||||
newtypes::{CommentId, CommunityId, DbUrl, InstanceId, PersonId, PostId, PostOrCommentId},
|
newtypes::{CommentId, CommunityId, DbUrl, InstanceId, PersonId, PostId, PostOrCommentId},
|
||||||
source::{
|
source::{
|
||||||
comment::{Comment, CommentLike, CommentUpdateForm},
|
comment::{Comment, CommentLike, CommentUpdateForm},
|
||||||
|
@ -38,6 +37,7 @@ use lemmy_db_schema::{
|
||||||
person::{Person, PersonUpdateForm},
|
person::{Person, PersonUpdateForm},
|
||||||
person_block::PersonBlock,
|
person_block::PersonBlock,
|
||||||
post::{Post, PostLike},
|
post::{Post, PostLike},
|
||||||
|
post_actions::{PostActions, PostActionsForm},
|
||||||
private_message::PrivateMessage,
|
private_message::PrivateMessage,
|
||||||
registration_application::RegistrationApplication,
|
registration_application::RegistrationApplication,
|
||||||
site::Site,
|
site::Site,
|
||||||
|
@ -158,13 +158,13 @@ pub async fn update_read_comments(
|
||||||
read_comments: i64,
|
read_comments: i64,
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
let person_post_agg_form = PersonPostAggregatesForm {
|
let person_post_agg_form = PostActionsForm {
|
||||||
person_id,
|
person_id,
|
||||||
post_id,
|
post_id,
|
||||||
read_comments,
|
read_comments,
|
||||||
};
|
};
|
||||||
|
|
||||||
PersonPostAggregates::upsert(pool, &person_post_agg_form).await?;
|
PostActions::upsert(pool, &person_post_agg_form).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ pub async fn create_comment(
|
||||||
update_read_comments(
|
update_read_comments(
|
||||||
local_user_view.person.id,
|
local_user_view.person.id,
|
||||||
post_id,
|
post_id,
|
||||||
post_view.counts.comments + 1,
|
post.comments + 1,
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub async fn get_post(
|
||||||
update_read_comments(
|
update_read_comments(
|
||||||
person_id,
|
person_id,
|
||||||
post_id,
|
post_id,
|
||||||
post_view.counts.comments,
|
post_view.post.comments,
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -28,7 +28,6 @@ use lemmy_api_common::{
|
||||||
utils::{check_post_deleted_or_removed, is_mod_or_admin},
|
utils::{check_post_deleted_or_removed, is_mod_or_admin},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::CommentAggregates,
|
|
||||||
newtypes::{PersonId, PostOrCommentId},
|
newtypes::{PersonId, PostOrCommentId},
|
||||||
source::{
|
source::{
|
||||||
activity::ActivitySendTargets,
|
activity::ActivitySendTargets,
|
||||||
|
@ -160,7 +159,7 @@ impl ActivityHandler for CreateOrUpdateNote {
|
||||||
CommentLike::like(&mut context.pool(), &like_form).await?;
|
CommentLike::like(&mut context.pool(), &like_form).await?;
|
||||||
|
|
||||||
// Calculate initial hot_rank
|
// Calculate initial hot_rank
|
||||||
CommentAggregates::update_hot_rank(&mut context.pool(), comment.id).await?;
|
Comment::update_hot_rank(&mut context.pool(), comment.id).await?;
|
||||||
|
|
||||||
let do_send_email = self.kind == CreateOrUpdateType::Create;
|
let do_send_email = self.kind == CreateOrUpdateType::Create;
|
||||||
let actor = self.actor.dereference(context).await?;
|
let actor = self.actor.dereference(context).await?;
|
||||||
|
|
|
@ -22,7 +22,6 @@ use activitypub_federation::{
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{build_response::send_local_notifs, context::LemmyContext};
|
use lemmy_api_common::{build_response::send_local_notifs, context::LemmyContext};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::PostAggregates,
|
|
||||||
newtypes::{PersonId, PostOrCommentId},
|
newtypes::{PersonId, PostOrCommentId},
|
||||||
source::{
|
source::{
|
||||||
activity::ActivitySendTargets,
|
activity::ActivitySendTargets,
|
||||||
|
@ -121,7 +120,7 @@ impl ActivityHandler for CreateOrUpdatePage {
|
||||||
PostLike::like(&mut context.pool(), &like_form).await?;
|
PostLike::like(&mut context.pool(), &like_form).await?;
|
||||||
|
|
||||||
// Calculate initial hot_rank for post
|
// Calculate initial hot_rank for post
|
||||||
PostAggregates::update_ranks(&mut context.pool(), post.id).await?;
|
Post::update_ranks(&mut context.pool(), post.id).await?;
|
||||||
|
|
||||||
let do_send_email = self.kind == CreateOrUpdateType::Create;
|
let do_send_email = self.kind == CreateOrUpdateType::Create;
|
||||||
let actor = self.actor.dereference(context).await?;
|
let actor = self.actor.dereference(context).await?;
|
||||||
|
|
|
@ -18,7 +18,6 @@ use lemmy_db_schema::{
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
instance_block::{InstanceBlock, InstanceBlockForm},
|
instance_block::{InstanceBlock, InstanceBlockForm},
|
||||||
local_user::{LocalUser, LocalUserUpdateForm},
|
local_user::{LocalUser, LocalUserUpdateForm},
|
||||||
local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeUpdateForm},
|
|
||||||
person::{Person, PersonUpdateForm},
|
person::{Person, PersonUpdateForm},
|
||||||
person_block::{PersonBlock, PersonBlockForm},
|
person_block::{PersonBlock, PersonBlockForm},
|
||||||
post::{PostSaved, PostSavedForm},
|
post::{PostSaved, PostSavedForm},
|
||||||
|
@ -55,7 +54,6 @@ pub struct UserSettingsBackup {
|
||||||
// TODO: might be worth making a separate struct for settings backup, to avoid breakage in case
|
// TODO: might be worth making a separate struct for settings backup, to avoid breakage in case
|
||||||
// fields are renamed, and to avoid storing unnecessary fields like person_id or email
|
// fields are renamed, and to avoid storing unnecessary fields like person_id or email
|
||||||
pub settings: Option<LocalUser>,
|
pub settings: Option<LocalUser>,
|
||||||
pub vote_display_mode_settings: Option<LocalUserVoteDisplayMode>,
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub followed_communities: Vec<ObjectId<ApubCommunity>>,
|
pub followed_communities: Vec<ObjectId<ApubCommunity>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -85,7 +83,6 @@ pub async fn export_settings(
|
||||||
matrix_id: local_user_view.person.matrix_user_id,
|
matrix_id: local_user_view.person.matrix_user_id,
|
||||||
bot_account: local_user_view.person.bot_account.into(),
|
bot_account: local_user_view.person.bot_account.into(),
|
||||||
settings: Some(local_user_view.local_user),
|
settings: Some(local_user_view.local_user),
|
||||||
vote_display_mode_settings: Some(local_user_view.local_user_vote_display_mode),
|
|
||||||
followed_communities: vec_into(lists.followed_communities),
|
followed_communities: vec_into(lists.followed_communities),
|
||||||
blocked_communities: vec_into(lists.blocked_communities),
|
blocked_communities: vec_into(lists.blocked_communities),
|
||||||
blocked_instances: lists.blocked_instances,
|
blocked_instances: lists.blocked_instances,
|
||||||
|
@ -130,6 +127,10 @@ pub async fn import_settings(
|
||||||
blur_nsfw: data.settings.as_ref().map(|s| s.blur_nsfw),
|
blur_nsfw: data.settings.as_ref().map(|s| s.blur_nsfw),
|
||||||
infinite_scroll_enabled: data.settings.as_ref().map(|s| s.infinite_scroll_enabled),
|
infinite_scroll_enabled: data.settings.as_ref().map(|s| s.infinite_scroll_enabled),
|
||||||
post_listing_mode: data.settings.as_ref().map(|s| s.post_listing_mode),
|
post_listing_mode: data.settings.as_ref().map(|s| s.post_listing_mode),
|
||||||
|
show_score: data.settings.as_ref().map(|s| s.show_score),
|
||||||
|
show_upvotes: data.settings.as_ref().map(|s| s.show_upvotes),
|
||||||
|
show_downvotes: data.settings.as_ref().map(|s| s.show_downvotes),
|
||||||
|
show_upvote_percentage: data.settings.as_ref().map(|s| s.show_upvote_percentage),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
LocalUser::update(
|
LocalUser::update(
|
||||||
|
@ -139,27 +140,6 @@ pub async fn import_settings(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Update the vote display mode settings
|
|
||||||
let vote_display_mode_form = LocalUserVoteDisplayModeUpdateForm {
|
|
||||||
score: data.vote_display_mode_settings.as_ref().map(|s| s.score),
|
|
||||||
upvotes: data.vote_display_mode_settings.as_ref().map(|s| s.upvotes),
|
|
||||||
downvotes: data
|
|
||||||
.vote_display_mode_settings
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| s.downvotes),
|
|
||||||
upvote_percentage: data
|
|
||||||
.vote_display_mode_settings
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| s.upvote_percentage),
|
|
||||||
};
|
|
||||||
|
|
||||||
LocalUserVoteDisplayMode::update(
|
|
||||||
&mut context.pool(),
|
|
||||||
local_user_view.local_user.id,
|
|
||||||
&vote_display_mode_form,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let url_count = data.followed_communities.len()
|
let url_count = data.followed_communities.len()
|
||||||
+ data.blocked_communities.len()
|
+ data.blocked_communities.len()
|
||||||
+ data.blocked_users.len()
|
+ data.blocked_users.len()
|
||||||
|
|
|
@ -9,7 +9,7 @@ use activitypub_federation::{
|
||||||
traits::Collection,
|
traits::Collection,
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{context::LemmyContext, utils::generate_followers_url};
|
use lemmy_api_common::{context::LemmyContext, utils::generate_followers_url};
|
||||||
use lemmy_db_schema::aggregates::structs::CommunityAggregates;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_db_views::structs::CommunityFollowerView;
|
use lemmy_db_views::structs::CommunityFollowerView;
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -54,12 +54,8 @@ impl Collection for ApubCommunityFollower {
|
||||||
community: &Self::Owner,
|
community: &Self::Owner,
|
||||||
context: &Data<Self::DataType>,
|
context: &Data<Self::DataType>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
CommunityAggregates::update_federated_followers(
|
Community::update_federated_followers(&mut context.pool(), community.id, json.total_items)
|
||||||
&mut context.pool(),
|
.await?;
|
||||||
community.id,
|
|
||||||
json.total_items,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(ApubCommunityFollower(()))
|
Ok(ApubCommunityFollower(()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
-- before the automatic deletion of the row that references it. This is not a problem for insert or delete.
|
-- before the automatic deletion of the row that references it. This is not a problem for insert or delete.
|
||||||
--
|
--
|
||||||
-- Triggers that update multiple tables should use this order: person_aggregates, comment_aggregates,
|
-- Triggers that update multiple tables should use this order: person_aggregates, comment_aggregates,
|
||||||
-- post_aggregates, community_aggregates, site_aggregates
|
-- post, community_aggregates, site_aggregates
|
||||||
-- * The order matters because the updated rows are locked until the end of the transaction, and statements
|
-- * The order matters because the updated rows are locked until the end of the transaction, and statements
|
||||||
-- in a trigger don't use separate transactions. This means that updates closer to the beginning cause
|
-- in a trigger don't use separate transactions. This means that updates closer to the beginning cause
|
||||||
-- longer locks because the duration of each update extends the durations of the locks caused by previous
|
-- longer locks because the duration of each update extends the durations of the locks caused by previous
|
||||||
|
@ -19,19 +19,6 @@
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- Create triggers for both post and comments
|
-- Create triggers for both post and comments
|
||||||
CREATE FUNCTION r.creator_id_from_post_aggregates (agg post_aggregates)
|
|
||||||
RETURNS int IMMUTABLE PARALLEL SAFE RETURN agg.creator_id;
|
|
||||||
|
|
||||||
CREATE FUNCTION r.creator_id_from_comment_aggregates (agg comment_aggregates)
|
|
||||||
RETURNS int IMMUTABLE PARALLEL SAFE RETURN (
|
|
||||||
SELECT
|
|
||||||
creator_id
|
|
||||||
FROM
|
|
||||||
comment
|
|
||||||
WHERE
|
|
||||||
comment.id = agg.comment_id LIMIT 1
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE PROCEDURE r.post_or_comment (table_name text)
|
CREATE PROCEDURE r.post_or_comment (table_name text)
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $a$
|
AS $a$
|
||||||
|
@ -41,7 +28,7 @@ BEGIN
|
||||||
CALL r.create_triggers ('thing_actions', $$
|
CALL r.create_triggers ('thing_actions', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
WITH thing_diff AS ( UPDATE
|
WITH thing_diff AS ( UPDATE
|
||||||
thing_aggregates AS a
|
thing AS a
|
||||||
SET
|
SET
|
||||||
score = a.score + diff.upvotes - diff.downvotes, upvotes = a.upvotes + diff.upvotes, downvotes = a.downvotes + diff.downvotes, controversy_rank = r.controversy_rank ((a.upvotes + diff.upvotes)::numeric, (a.downvotes + diff.downvotes)::numeric)
|
score = a.score + diff.upvotes - diff.downvotes, upvotes = a.upvotes + diff.upvotes, downvotes = a.downvotes + diff.downvotes, controversy_rank = r.controversy_rank ((a.upvotes + diff.upvotes)::numeric, (a.downvotes + diff.downvotes)::numeric)
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -49,18 +36,18 @@ BEGIN
|
||||||
(thing_actions).thing_id, coalesce(sum(count_diff) FILTER (WHERE (thing_actions).like_score = 1), 0) AS upvotes, coalesce(sum(count_diff) FILTER (WHERE (thing_actions).like_score != 1), 0) AS downvotes FROM select_old_and_new_rows AS old_and_new_rows
|
(thing_actions).thing_id, coalesce(sum(count_diff) FILTER (WHERE (thing_actions).like_score = 1), 0) AS upvotes, coalesce(sum(count_diff) FILTER (WHERE (thing_actions).like_score != 1), 0) AS downvotes FROM select_old_and_new_rows AS old_and_new_rows
|
||||||
WHERE (thing_actions).like_score IS NOT NULL GROUP BY (thing_actions).thing_id) AS diff
|
WHERE (thing_actions).like_score IS NOT NULL GROUP BY (thing_actions).thing_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.thing_id = diff.thing_id
|
a.id = diff.thing_id
|
||||||
AND (diff.upvotes, diff.downvotes) != (0, 0)
|
AND (diff.upvotes, diff.downvotes) != (0, 0)
|
||||||
RETURNING
|
RETURNING
|
||||||
r.creator_id_from_thing_aggregates (a.*) AS creator_id, diff.upvotes - diff.downvotes AS score)
|
a.creator_id AS creator_id, diff.upvotes - diff.downvotes AS score)
|
||||||
UPDATE
|
UPDATE
|
||||||
person_aggregates AS a
|
person AS a
|
||||||
SET
|
SET
|
||||||
thing_score = a.thing_score + diff.score FROM (
|
thing_score = a.thing_score + diff.score FROM (
|
||||||
SELECT
|
SELECT
|
||||||
creator_id, sum(score) AS score FROM thing_diff GROUP BY creator_id) AS diff
|
creator_id, sum(score) AS score FROM thing_diff GROUP BY creator_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.person_id = diff.creator_id
|
a.id = diff.creator_id
|
||||||
AND diff.score != 0;
|
AND diff.score != 0;
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END;
|
END;
|
||||||
|
@ -93,23 +80,35 @@ END;
|
||||||
|
|
||||||
CALL r.create_triggers ('comment', $$
|
CALL r.create_triggers ('comment', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE
|
-- Prevent infinite recursion
|
||||||
person_aggregates AS a
|
IF (
|
||||||
SET
|
|
||||||
comment_count = a.comment_count + diff.comment_count
|
|
||||||
FROM (
|
|
||||||
SELECT
|
SELECT
|
||||||
(comment).creator_id, coalesce(sum(count_diff), 0) AS comment_count
|
count(*)
|
||||||
FROM select_old_and_new_rows AS old_and_new_rows
|
FROM select_old_and_new_rows AS old_and_new_rows) = 0 THEN
|
||||||
WHERE
|
RETURN NULL;
|
||||||
r.is_counted (comment)
|
|
||||||
GROUP BY (comment).creator_id) AS diff
|
END IF;
|
||||||
WHERE
|
|
||||||
a.person_id = diff.creator_id
|
|
||||||
AND diff.comment_count != 0;
|
|
||||||
|
|
||||||
UPDATE
|
UPDATE
|
||||||
comment_aggregates AS a
|
person AS a
|
||||||
|
SET
|
||||||
|
comment_count = a.comment_count + diff.comment_count
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
(comment).creator_id,
|
||||||
|
coalesce(sum(count_diff), 0) AS comment_count
|
||||||
|
FROM
|
||||||
|
select_old_and_new_rows AS old_and_new_rows
|
||||||
|
WHERE
|
||||||
|
r.is_counted (comment)
|
||||||
|
GROUP BY
|
||||||
|
(comment).creator_id) AS diff
|
||||||
|
WHERE
|
||||||
|
a.id = diff.creator_id
|
||||||
|
AND diff.comment_count != 0;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
comment AS a
|
||||||
SET
|
SET
|
||||||
child_count = a.child_count + diff.child_count
|
child_count = a.child_count + diff.child_count
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -143,66 +142,43 @@ FROM (
|
||||||
GROUP BY
|
GROUP BY
|
||||||
parent_id) AS diff
|
parent_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.comment_id = diff.parent_id
|
a.id = diff.parent_id
|
||||||
AND diff.child_count != 0;
|
AND diff.child_count != 0;
|
||||||
|
|
||||||
WITH post_diff AS (
|
|
||||||
UPDATE
|
|
||||||
post_aggregates AS a
|
|
||||||
SET
|
|
||||||
comments = a.comments + diff.comments,
|
|
||||||
newest_comment_time = GREATEST (a.newest_comment_time, diff.newest_comment_time),
|
|
||||||
newest_comment_time_necro = GREATEST (a.newest_comment_time_necro, diff.newest_comment_time_necro)
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
post.id AS post_id,
|
|
||||||
coalesce(sum(count_diff), 0) AS comments,
|
|
||||||
-- Old rows are excluded using `count_diff = 1`
|
|
||||||
max((comment).published) FILTER (WHERE count_diff = 1) AS newest_comment_time,
|
|
||||||
max((comment).published) FILTER (WHERE count_diff = 1
|
|
||||||
-- Ignore comments from the post's creator
|
|
||||||
AND post.creator_id != (comment).creator_id
|
|
||||||
-- Ignore comments on old posts
|
|
||||||
AND post.published > ((comment).published - '2 days'::interval)) AS newest_comment_time_necro,
|
|
||||||
r.is_counted (post.*) AS include_in_community_aggregates
|
|
||||||
FROM
|
|
||||||
select_old_and_new_rows AS old_and_new_rows
|
|
||||||
LEFT JOIN post ON post.id = (comment).post_id
|
|
||||||
WHERE
|
|
||||||
r.is_counted (comment)
|
|
||||||
GROUP BY
|
|
||||||
post.id) AS diff
|
|
||||||
WHERE
|
|
||||||
a.post_id = diff.post_id
|
|
||||||
AND (diff.comments,
|
|
||||||
GREATEST (a.newest_comment_time, diff.newest_comment_time),
|
|
||||||
GREATEST (a.newest_comment_time_necro, diff.newest_comment_time_necro)) != (0,
|
|
||||||
a.newest_comment_time,
|
|
||||||
a.newest_comment_time_necro)
|
|
||||||
RETURNING
|
|
||||||
a.community_id,
|
|
||||||
diff.comments,
|
|
||||||
diff.include_in_community_aggregates)
|
|
||||||
UPDATE
|
UPDATE
|
||||||
community_aggregates AS a
|
post AS a
|
||||||
SET
|
SET
|
||||||
comments = a.comments + diff.comments
|
comments = a.comments + diff.comments,
|
||||||
|
newest_comment_time = GREATEST (a.newest_comment_time, diff.newest_comment_time),
|
||||||
|
newest_comment_time_necro = GREATEST (a.newest_comment_time_necro, diff.newest_comment_time_necro)
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
community_id,
|
post.id AS post_id,
|
||||||
sum(comments) AS comments
|
coalesce(sum(count_diff), 0) AS comments,
|
||||||
FROM
|
-- Old rows are excluded using `count_diff = 1`
|
||||||
post_diff
|
max((comment).published) FILTER (WHERE count_diff = 1) AS newest_comment_time,
|
||||||
WHERE
|
max((comment).published) FILTER (WHERE count_diff = 1
|
||||||
post_diff.include_in_community_aggregates
|
-- Ignore comments from the post's creator
|
||||||
GROUP BY
|
AND post.creator_id != (comment).creator_id
|
||||||
community_id) AS diff
|
-- Ignore comments on old posts
|
||||||
|
AND post.published > ((comment).published - '2 days'::interval)) AS newest_comment_time_necro
|
||||||
|
FROM
|
||||||
|
select_old_and_new_rows AS old_and_new_rows
|
||||||
|
LEFT JOIN post ON post.id = (comment).post_id
|
||||||
WHERE
|
WHERE
|
||||||
a.community_id = diff.community_id
|
r.is_counted (comment)
|
||||||
AND diff.comments != 0;
|
GROUP BY
|
||||||
|
post.id) AS diff
|
||||||
|
WHERE
|
||||||
|
a.id = diff.post_id
|
||||||
|
AND (diff.comments,
|
||||||
|
GREATEST (a.newest_comment_time, diff.newest_comment_time),
|
||||||
|
GREATEST (a.newest_comment_time_necro, diff.newest_comment_time_necro)) != (0,
|
||||||
|
a.newest_comment_time,
|
||||||
|
a.newest_comment_time_necro);
|
||||||
|
|
||||||
UPDATE
|
UPDATE
|
||||||
site_aggregates AS a
|
local_site AS a
|
||||||
SET
|
SET
|
||||||
comments = a.comments + diff.comments
|
comments = a.comments + diff.comments
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -225,7 +201,7 @@ $$);
|
||||||
CALL r.create_triggers ('post', $$
|
CALL r.create_triggers ('post', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE
|
UPDATE
|
||||||
person_aggregates AS a
|
person AS a
|
||||||
SET
|
SET
|
||||||
post_count = a.post_count + diff.post_count
|
post_count = a.post_count + diff.post_count
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -236,17 +212,19 @@ BEGIN
|
||||||
r.is_counted (post)
|
r.is_counted (post)
|
||||||
GROUP BY (post).creator_id) AS diff
|
GROUP BY (post).creator_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.person_id = diff.creator_id
|
a.id = diff.creator_id
|
||||||
AND diff.post_count != 0;
|
AND diff.post_count != 0;
|
||||||
|
|
||||||
UPDATE
|
UPDATE
|
||||||
community_aggregates AS a
|
community AS a
|
||||||
SET
|
SET
|
||||||
posts = a.posts + diff.posts
|
posts = a.posts + diff.posts,
|
||||||
|
comments = a.comments + diff.comments
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
(post).community_id,
|
(post).community_id,
|
||||||
coalesce(sum(count_diff), 0) AS posts
|
coalesce(sum(count_diff), 0) AS posts,
|
||||||
|
coalesce(sum(count_diff * (post).comments), 0) AS comments
|
||||||
FROM
|
FROM
|
||||||
select_old_and_new_rows AS old_and_new_rows
|
select_old_and_new_rows AS old_and_new_rows
|
||||||
WHERE
|
WHERE
|
||||||
|
@ -254,11 +232,13 @@ FROM (
|
||||||
GROUP BY
|
GROUP BY
|
||||||
(post).community_id) AS diff
|
(post).community_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.community_id = diff.community_id
|
a.id = diff.community_id
|
||||||
AND diff.posts != 0;
|
AND (diff.posts,
|
||||||
|
diff.comments) != (0,
|
||||||
|
0);
|
||||||
|
|
||||||
UPDATE
|
UPDATE
|
||||||
site_aggregates AS a
|
local_site AS a
|
||||||
SET
|
SET
|
||||||
posts = a.posts + diff.posts
|
posts = a.posts + diff.posts
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -281,7 +261,7 @@ $$);
|
||||||
CALL r.create_triggers ('community', $$
|
CALL r.create_triggers ('community', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE
|
UPDATE
|
||||||
site_aggregates AS a
|
local_site AS a
|
||||||
SET
|
SET
|
||||||
communities = a.communities + diff.communities
|
communities = a.communities + diff.communities
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -303,7 +283,7 @@ $$);
|
||||||
CALL r.create_triggers ('person', $$
|
CALL r.create_triggers ('person', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE
|
UPDATE
|
||||||
site_aggregates AS a
|
local_site AS a
|
||||||
SET
|
SET
|
||||||
users = a.users + diff.users
|
users = a.users + diff.users
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -320,51 +300,13 @@ END;
|
||||||
|
|
||||||
$$);
|
$$);
|
||||||
|
|
||||||
-- For community_aggregates.comments, don't include comments of deleted or removed posts
|
|
||||||
CREATE FUNCTION r.update_comment_count_from_post ()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
UPDATE
|
|
||||||
community_aggregates AS a
|
|
||||||
SET
|
|
||||||
comments = a.comments + diff.comments
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
old_post.community_id,
|
|
||||||
sum((
|
|
||||||
CASE WHEN r.is_counted (new_post.*) THEN
|
|
||||||
1
|
|
||||||
ELSE
|
|
||||||
-1
|
|
||||||
END) * post_aggregates.comments) AS comments
|
|
||||||
FROM
|
|
||||||
new_post
|
|
||||||
INNER JOIN old_post ON new_post.id = old_post.id
|
|
||||||
AND (r.is_counted (new_post.*) != r.is_counted (old_post.*))
|
|
||||||
INNER JOIN post_aggregates ON post_aggregates.post_id = new_post.id
|
|
||||||
GROUP BY
|
|
||||||
old_post.community_id) AS diff
|
|
||||||
WHERE
|
|
||||||
a.community_id = diff.community_id
|
|
||||||
AND diff.comments != 0;
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE TRIGGER comment_count
|
|
||||||
AFTER UPDATE ON post REFERENCING OLD TABLE AS old_post NEW TABLE AS new_post
|
|
||||||
FOR EACH STATEMENT
|
|
||||||
EXECUTE FUNCTION r.update_comment_count_from_post ();
|
|
||||||
|
|
||||||
-- Count subscribers for communities.
|
-- Count subscribers for communities.
|
||||||
-- subscribers should be updated only when a local community is followed by a local or remote person.
|
-- subscribers should be updated only when a local community is followed by a local or remote person.
|
||||||
-- subscribers_local should be updated only when a local person follows a local or remote community.
|
-- subscribers_local should be updated only when a local person follows a local or remote community.
|
||||||
CALL r.create_triggers ('community_actions', $$
|
CALL r.create_triggers ('community_actions', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE
|
UPDATE
|
||||||
community_aggregates AS a
|
community AS a
|
||||||
SET
|
SET
|
||||||
subscribers = a.subscribers + diff.subscribers, subscribers_local = a.subscribers_local + diff.subscribers_local
|
subscribers = a.subscribers + diff.subscribers, subscribers_local = a.subscribers_local + diff.subscribers_local
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -375,7 +317,7 @@ BEGIN
|
||||||
LEFT JOIN person ON person.id = (community_actions).person_id
|
LEFT JOIN person ON person.id = (community_actions).person_id
|
||||||
WHERE (community_actions).followed IS NOT NULL GROUP BY (community_actions).community_id) AS diff
|
WHERE (community_actions).followed IS NOT NULL GROUP BY (community_actions).community_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.community_id = diff.community_id
|
a.id = diff.community_id
|
||||||
AND (diff.subscribers, diff.subscribers_local) != (0, 0);
|
AND (diff.subscribers, diff.subscribers_local) != (0, 0);
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
@ -387,7 +329,7 @@ $$);
|
||||||
CALL r.create_triggers ('post_report', $$
|
CALL r.create_triggers ('post_report', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE
|
UPDATE
|
||||||
post_aggregates AS a
|
post AS a
|
||||||
SET
|
SET
|
||||||
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -396,7 +338,7 @@ BEGIN
|
||||||
AND NOT (post_report).violates_instance_rules), 0) AS unresolved_report_count
|
AND NOT (post_report).violates_instance_rules), 0) AS unresolved_report_count
|
||||||
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (post_report).post_id) AS diff
|
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (post_report).post_id) AS diff
|
||||||
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
||||||
AND a.post_id = diff.post_id;
|
AND a.id = diff.post_id;
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
|
||||||
|
@ -407,7 +349,7 @@ $$);
|
||||||
CALL r.create_triggers ('comment_report', $$
|
CALL r.create_triggers ('comment_report', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE
|
UPDATE
|
||||||
comment_aggregates AS a
|
comment AS a
|
||||||
SET
|
SET
|
||||||
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -416,7 +358,7 @@ BEGIN
|
||||||
AND NOT (comment_report).violates_instance_rules), 0) AS unresolved_report_count
|
AND NOT (comment_report).violates_instance_rules), 0) AS unresolved_report_count
|
||||||
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (comment_report).comment_id) AS diff
|
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (comment_report).comment_id) AS diff
|
||||||
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
||||||
AND a.comment_id = diff.comment_id;
|
AND a.id = diff.comment_id;
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
|
||||||
|
@ -427,7 +369,7 @@ $$);
|
||||||
CALL r.create_triggers ('community_report', $$
|
CALL r.create_triggers ('community_report', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE
|
UPDATE
|
||||||
community_aggregates AS a
|
community AS a
|
||||||
SET
|
SET
|
||||||
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -435,7 +377,7 @@ BEGIN
|
||||||
(community_report).community_id, coalesce(sum(count_diff), 0) AS report_count, coalesce(sum(count_diff) FILTER (WHERE NOT (community_report).resolved), 0) AS unresolved_report_count
|
(community_report).community_id, coalesce(sum(count_diff), 0) AS report_count, coalesce(sum(count_diff) FILTER (WHERE NOT (community_report).resolved), 0) AS unresolved_report_count
|
||||||
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (community_report).community_id) AS diff
|
FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (community_report).community_id) AS diff
|
||||||
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0)
|
||||||
AND a.community_id = diff.community_id;
|
AND a.id = diff.community_id;
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
|
||||||
|
@ -443,160 +385,7 @@ END;
|
||||||
|
|
||||||
$$);
|
$$);
|
||||||
|
|
||||||
-- These triggers create and update rows in each aggregates table to match its associated table's rows.
|
|
||||||
-- Deleting rows and updating IDs are already handled by `CASCADE` in foreign key constraints.
|
|
||||||
CREATE FUNCTION r.comment_aggregates_from_comment ()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO comment_aggregates (comment_id, published)
|
|
||||||
SELECT
|
|
||||||
id,
|
|
||||||
published
|
|
||||||
FROM
|
|
||||||
new_comment;
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE TRIGGER aggregates
|
|
||||||
AFTER INSERT ON comment REFERENCING NEW TABLE AS new_comment
|
|
||||||
FOR EACH STATEMENT
|
|
||||||
EXECUTE FUNCTION r.comment_aggregates_from_comment ();
|
|
||||||
|
|
||||||
CREATE FUNCTION r.community_aggregates_from_community ()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO community_aggregates (community_id, published)
|
|
||||||
SELECT
|
|
||||||
id,
|
|
||||||
published
|
|
||||||
FROM
|
|
||||||
new_community;
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE TRIGGER aggregates
|
|
||||||
AFTER INSERT ON community REFERENCING NEW TABLE AS new_community
|
|
||||||
FOR EACH STATEMENT
|
|
||||||
EXECUTE FUNCTION r.community_aggregates_from_community ();
|
|
||||||
|
|
||||||
CREATE FUNCTION r.person_aggregates_from_person ()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO person_aggregates (person_id)
|
|
||||||
SELECT
|
|
||||||
id
|
|
||||||
FROM
|
|
||||||
new_person;
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE TRIGGER aggregates
|
|
||||||
AFTER INSERT ON person REFERENCING NEW TABLE AS new_person
|
|
||||||
FOR EACH STATEMENT
|
|
||||||
EXECUTE FUNCTION r.person_aggregates_from_person ();
|
|
||||||
|
|
||||||
CREATE FUNCTION r.post_aggregates_from_post ()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO post_aggregates (post_id, published, newest_comment_time, newest_comment_time_necro, community_id, creator_id, instance_id, featured_community, featured_local)
|
|
||||||
SELECT
|
|
||||||
new_post.id,
|
|
||||||
new_post.published,
|
|
||||||
new_post.published,
|
|
||||||
new_post.published,
|
|
||||||
new_post.community_id,
|
|
||||||
new_post.creator_id,
|
|
||||||
community.instance_id,
|
|
||||||
new_post.featured_community,
|
|
||||||
new_post.featured_local
|
|
||||||
FROM
|
|
||||||
new_post
|
|
||||||
INNER JOIN community ON community.id = new_post.community_id;
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE TRIGGER aggregates
|
|
||||||
AFTER INSERT ON post REFERENCING NEW TABLE AS new_post
|
|
||||||
FOR EACH STATEMENT
|
|
||||||
EXECUTE FUNCTION r.post_aggregates_from_post ();
|
|
||||||
|
|
||||||
CREATE FUNCTION r.post_aggregates_from_post_update ()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
UPDATE
|
|
||||||
post_aggregates
|
|
||||||
SET
|
|
||||||
featured_community = new_post.featured_community,
|
|
||||||
featured_local = new_post.featured_local
|
|
||||||
FROM
|
|
||||||
new_post
|
|
||||||
INNER JOIN old_post ON old_post.id = new_post.id
|
|
||||||
AND (old_post.featured_community,
|
|
||||||
old_post.featured_local) != (new_post.featured_community,
|
|
||||||
new_post.featured_local)
|
|
||||||
WHERE
|
|
||||||
post_aggregates.post_id = new_post.id;
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE TRIGGER aggregates_update
|
|
||||||
AFTER UPDATE ON post REFERENCING OLD TABLE AS old_post NEW TABLE AS new_post
|
|
||||||
FOR EACH STATEMENT
|
|
||||||
EXECUTE FUNCTION r.post_aggregates_from_post_update ();
|
|
||||||
|
|
||||||
CREATE FUNCTION r.site_aggregates_from_site ()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
-- only 1 row can be in site_aggregates because of the index idx_site_aggregates_1_row_only.
|
|
||||||
-- we only ever want to have a single value in site_aggregate because the site_aggregate triggers update all rows in that table.
|
|
||||||
-- a cleaner check would be to insert it for the local_site but that would break assumptions at least in the tests
|
|
||||||
INSERT INTO site_aggregates (site_id)
|
|
||||||
VALUES (NEW.id)
|
|
||||||
ON CONFLICT ((TRUE))
|
|
||||||
DO NOTHING;
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE TRIGGER aggregates
|
|
||||||
AFTER INSERT ON site
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION r.site_aggregates_from_site ();
|
|
||||||
|
|
||||||
-- Change the order of some cascading deletions to make deletion triggers run before the deletion of rows that the triggers need to read
|
-- Change the order of some cascading deletions to make deletion triggers run before the deletion of rows that the triggers need to read
|
||||||
CREATE FUNCTION r.delete_comments_before_post ()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
DELETE FROM comment AS c
|
|
||||||
WHERE c.post_id = OLD.id;
|
|
||||||
RETURN OLD;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE TRIGGER delete_comments
|
|
||||||
BEFORE DELETE ON post
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION r.delete_comments_before_post ();
|
|
||||||
|
|
||||||
CREATE FUNCTION r.delete_follow_before_person ()
|
CREATE FUNCTION r.delete_follow_before_person ()
|
||||||
RETURNS TRIGGER
|
RETURNS TRIGGER
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
|
@ -647,6 +436,16 @@ BEGIN
|
||||||
IF NEW.local THEN
|
IF NEW.local THEN
|
||||||
NEW.ap_id = coalesce(NEW.ap_id, r.local_url ('/post/' || NEW.id::text));
|
NEW.ap_id = coalesce(NEW.ap_id, r.local_url ('/post/' || NEW.id::text));
|
||||||
END IF;
|
END IF;
|
||||||
|
-- Set aggregates
|
||||||
|
NEW.newest_comment_time = NEW.published;
|
||||||
|
NEW.newest_comment_time_necro = NEW.published;
|
||||||
|
NEW.instance_id = (
|
||||||
|
SELECT
|
||||||
|
community.instance_id
|
||||||
|
FROM
|
||||||
|
community
|
||||||
|
WHERE
|
||||||
|
community.id = NEW.community_id);
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
@ -963,7 +762,7 @@ CALL r.create_search_combined_trigger ('community');
|
||||||
CALL r.create_search_combined_trigger ('person');
|
CALL r.create_search_combined_trigger ('person');
|
||||||
|
|
||||||
-- You also need to triggers to update the `score` column.
|
-- You also need to triggers to update the `score` column.
|
||||||
-- post | post_aggregates::score
|
-- post | post::score
|
||||||
-- comment | comment_aggregates::score
|
-- comment | comment_aggregates::score
|
||||||
-- community | community_aggregates::users_active_monthly
|
-- community | community_aggregates::users_active_monthly
|
||||||
-- person | person_aggregates::post_score
|
-- person | person_aggregates::post_score
|
||||||
|
@ -979,13 +778,13 @@ BEGIN
|
||||||
SET
|
SET
|
||||||
score = NEW.score
|
score = NEW.score
|
||||||
WHERE
|
WHERE
|
||||||
post_id = NEW.post_id;
|
post_id = NEW.id;
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
CREATE TRIGGER search_combined_post_score
|
CREATE TRIGGER search_combined_post_score
|
||||||
AFTER UPDATE OF score ON post_aggregates
|
AFTER UPDATE OF score ON post
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE FUNCTION r.search_combined_post_score_update ();
|
EXECUTE FUNCTION r.search_combined_post_score_update ();
|
||||||
|
|
||||||
|
@ -1000,13 +799,13 @@ BEGIN
|
||||||
SET
|
SET
|
||||||
score = NEW.score
|
score = NEW.score
|
||||||
WHERE
|
WHERE
|
||||||
comment_id = NEW.comment_id;
|
comment_id = NEW.id;
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
CREATE TRIGGER search_combined_comment_score
|
CREATE TRIGGER search_combined_comment_score
|
||||||
AFTER UPDATE OF score ON comment_aggregates
|
AFTER UPDATE OF score ON comment
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE FUNCTION r.search_combined_comment_score_update ();
|
EXECUTE FUNCTION r.search_combined_comment_score_update ();
|
||||||
|
|
||||||
|
@ -1021,13 +820,13 @@ BEGIN
|
||||||
SET
|
SET
|
||||||
score = NEW.post_score
|
score = NEW.post_score
|
||||||
WHERE
|
WHERE
|
||||||
person_id = NEW.person_id;
|
person_id = NEW.id;
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
CREATE TRIGGER search_combined_person_score
|
CREATE TRIGGER search_combined_person_score
|
||||||
AFTER UPDATE OF post_score ON person_aggregates
|
AFTER UPDATE OF post_score ON person
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE FUNCTION r.search_combined_person_score_update ();
|
EXECUTE FUNCTION r.search_combined_person_score_update ();
|
||||||
|
|
||||||
|
@ -1042,13 +841,13 @@ BEGIN
|
||||||
SET
|
SET
|
||||||
score = NEW.users_active_month
|
score = NEW.users_active_month
|
||||||
WHERE
|
WHERE
|
||||||
community_id = NEW.community_id;
|
community_id = NEW.id;
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
CREATE TRIGGER search_combined_community_score
|
CREATE TRIGGER search_combined_community_score
|
||||||
AFTER UPDATE OF users_active_month ON community_aggregates
|
AFTER UPDATE OF users_active_month ON community
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE FUNCTION r.search_combined_community_score_update ();
|
EXECUTE FUNCTION r.search_combined_community_score_update ();
|
||||||
|
|
||||||
|
|
|
@ -225,7 +225,7 @@ BEGIN
|
||||||
COALESCE(sum(comments + upvotes + downvotes)::bigint, 0) AS count_,
|
COALESCE(sum(comments + upvotes + downvotes)::bigint, 0) AS count_,
|
||||||
community_id AS community_id_
|
community_id AS community_id_
|
||||||
FROM
|
FROM
|
||||||
post_aggregates
|
post
|
||||||
WHERE
|
WHERE
|
||||||
published >= (CURRENT_TIMESTAMP - i::interval)
|
published >= (CURRENT_TIMESTAMP - i::interval)
|
||||||
GROUP BY
|
GROUP BY
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
use crate::{
|
|
||||||
aggregates::structs::CommentAggregates,
|
|
||||||
newtypes::CommentId,
|
|
||||||
schema::comment_aggregates,
|
|
||||||
utils::{functions::hot_rank, get_conn, DbPool},
|
|
||||||
};
|
|
||||||
use diesel::{result::Error, ExpressionMethods, QueryDsl};
|
|
||||||
use diesel_async::RunQueryDsl;
|
|
||||||
|
|
||||||
impl CommentAggregates {
|
|
||||||
pub async fn read(pool: &mut DbPool<'_>, comment_id: CommentId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
comment_aggregates::table.find(comment_id).first(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_hot_rank(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
comment_id: CommentId,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
|
|
||||||
diesel::update(comment_aggregates::table.find(comment_id))
|
|
||||||
.set(comment_aggregates::hot_rank.eq(hot_rank(
|
|
||||||
comment_aggregates::score,
|
|
||||||
comment_aggregates::published,
|
|
||||||
)))
|
|
||||||
.get_result::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
aggregates::comment_aggregates::CommentAggregates,
|
|
||||||
source::{
|
|
||||||
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm},
|
|
||||||
community::{Community, CommunityInsertForm},
|
|
||||||
instance::Instance,
|
|
||||||
person::{Person, PersonInsertForm},
|
|
||||||
post::{Post, PostInsertForm},
|
|
||||||
},
|
|
||||||
traits::{Crud, Likeable},
|
|
||||||
utils::build_db_pool_for_tests,
|
|
||||||
};
|
|
||||||
use diesel::result::Error;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use serial_test::serial;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_crud() -> Result<(), Error> {
|
|
||||||
let pool = &build_db_pool_for_tests();
|
|
||||||
let pool = &mut pool.into();
|
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_comment_agg");
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await?;
|
|
||||||
|
|
||||||
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_comment_agg");
|
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await?;
|
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
|
||||||
inserted_instance.id,
|
|
||||||
"TIL_comment_agg".into(),
|
|
||||||
"nada".to_owned(),
|
|
||||||
"pubkey".to_string(),
|
|
||||||
);
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await?;
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
|
||||||
"A test post".into(),
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_community.id,
|
|
||||||
);
|
|
||||||
let inserted_post = Post::create(pool, &new_post).await?;
|
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let _inserted_child_comment =
|
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
|
||||||
|
|
||||||
let comment_like = CommentLikeForm {
|
|
||||||
comment_id: inserted_comment.id,
|
|
||||||
person_id: inserted_person.id,
|
|
||||||
score: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
CommentLike::like(pool, &comment_like).await?;
|
|
||||||
|
|
||||||
let comment_aggs_before_delete = CommentAggregates::read(pool, inserted_comment.id).await?;
|
|
||||||
|
|
||||||
assert_eq!(1, comment_aggs_before_delete.score);
|
|
||||||
assert_eq!(1, comment_aggs_before_delete.upvotes);
|
|
||||||
assert_eq!(0, comment_aggs_before_delete.downvotes);
|
|
||||||
|
|
||||||
// Add a post dislike from the other person
|
|
||||||
let comment_dislike = CommentLikeForm {
|
|
||||||
comment_id: inserted_comment.id,
|
|
||||||
person_id: another_inserted_person.id,
|
|
||||||
score: -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
CommentLike::like(pool, &comment_dislike).await?;
|
|
||||||
|
|
||||||
let comment_aggs_after_dislike = CommentAggregates::read(pool, inserted_comment.id).await?;
|
|
||||||
|
|
||||||
assert_eq!(0, comment_aggs_after_dislike.score);
|
|
||||||
assert_eq!(1, comment_aggs_after_dislike.upvotes);
|
|
||||||
assert_eq!(1, comment_aggs_after_dislike.downvotes);
|
|
||||||
|
|
||||||
// Remove the first comment like
|
|
||||||
CommentLike::remove(pool, inserted_person.id, inserted_comment.id).await?;
|
|
||||||
let after_like_remove = CommentAggregates::read(pool, inserted_comment.id).await?;
|
|
||||||
assert_eq!(-1, after_like_remove.score);
|
|
||||||
assert_eq!(0, after_like_remove.upvotes);
|
|
||||||
assert_eq!(1, after_like_remove.downvotes);
|
|
||||||
|
|
||||||
// Remove the parent post
|
|
||||||
Post::delete(pool, inserted_post.id).await?;
|
|
||||||
|
|
||||||
// Should be none found, since the post was deleted
|
|
||||||
let after_delete = CommentAggregates::read(pool, inserted_comment.id).await;
|
|
||||||
assert!(after_delete.is_err());
|
|
||||||
|
|
||||||
// This should delete all the associated rows, and fire triggers
|
|
||||||
Person::delete(pool, another_inserted_person.id).await?;
|
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
|
||||||
assert_eq!(1, person_num_deleted);
|
|
||||||
|
|
||||||
// Delete the community
|
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
|
||||||
assert_eq!(1, community_num_deleted);
|
|
||||||
|
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
use crate::{
|
|
||||||
aggregates::structs::CommunityAggregates,
|
|
||||||
newtypes::CommunityId,
|
|
||||||
schema::{community_aggregates, community_aggregates::subscribers},
|
|
||||||
utils::{get_conn, DbPool},
|
|
||||||
};
|
|
||||||
use diesel::{result::Error, ExpressionMethods, QueryDsl};
|
|
||||||
use diesel_async::RunQueryDsl;
|
|
||||||
|
|
||||||
impl CommunityAggregates {
|
|
||||||
pub async fn read(pool: &mut DbPool<'_>, for_community_id: CommunityId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
community_aggregates::table
|
|
||||||
.find(for_community_id)
|
|
||||||
.first(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_federated_followers(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
for_community_id: CommunityId,
|
|
||||||
new_subscribers: i32,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
let new_subscribers: i64 = new_subscribers.into();
|
|
||||||
diesel::update(community_aggregates::table.find(for_community_id))
|
|
||||||
.set(subscribers.eq(new_subscribers))
|
|
||||||
.get_result(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
aggregates::community_aggregates::CommunityAggregates,
|
|
||||||
source::{
|
|
||||||
comment::{Comment, CommentInsertForm},
|
|
||||||
community::{
|
|
||||||
Community,
|
|
||||||
CommunityFollower,
|
|
||||||
CommunityFollowerForm,
|
|
||||||
CommunityFollowerState,
|
|
||||||
CommunityInsertForm,
|
|
||||||
},
|
|
||||||
instance::Instance,
|
|
||||||
person::{Person, PersonInsertForm},
|
|
||||||
post::{Post, PostInsertForm},
|
|
||||||
},
|
|
||||||
traits::{Crud, Followable},
|
|
||||||
utils::build_db_pool_for_tests,
|
|
||||||
};
|
|
||||||
use diesel::result::Error;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use serial_test::serial;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_crud() -> Result<(), Error> {
|
|
||||||
let pool = &build_db_pool_for_tests();
|
|
||||||
let pool = &mut pool.into();
|
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await?;
|
|
||||||
|
|
||||||
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await?;
|
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
|
||||||
inserted_instance.id,
|
|
||||||
"TIL_community_agg".into(),
|
|
||||||
"nada".to_owned(),
|
|
||||||
"pubkey".to_string(),
|
|
||||||
);
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await?;
|
|
||||||
|
|
||||||
let another_community = CommunityInsertForm::new(
|
|
||||||
inserted_instance.id,
|
|
||||||
"TIL_community_agg_2".into(),
|
|
||||||
"nada".to_owned(),
|
|
||||||
"pubkey".to_string(),
|
|
||||||
);
|
|
||||||
let another_inserted_community = Community::create(pool, &another_community).await?;
|
|
||||||
|
|
||||||
let first_person_follow = CommunityFollowerForm {
|
|
||||||
community_id: inserted_community.id,
|
|
||||||
person_id: inserted_person.id,
|
|
||||||
state: Some(CommunityFollowerState::Accepted),
|
|
||||||
approver_id: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
CommunityFollower::follow(pool, &first_person_follow).await?;
|
|
||||||
|
|
||||||
let second_person_follow = CommunityFollowerForm {
|
|
||||||
community_id: inserted_community.id,
|
|
||||||
person_id: another_inserted_person.id,
|
|
||||||
state: Some(CommunityFollowerState::Accepted),
|
|
||||||
approver_id: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
CommunityFollower::follow(pool, &second_person_follow).await?;
|
|
||||||
|
|
||||||
let another_community_follow = CommunityFollowerForm {
|
|
||||||
community_id: another_inserted_community.id,
|
|
||||||
person_id: inserted_person.id,
|
|
||||||
state: Some(CommunityFollowerState::Accepted),
|
|
||||||
approver_id: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
CommunityFollower::follow(pool, &another_community_follow).await?;
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
|
||||||
"A test post".into(),
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_community.id,
|
|
||||||
);
|
|
||||||
let inserted_post = Post::create(pool, &new_post).await?;
|
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let _inserted_child_comment =
|
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
|
||||||
|
|
||||||
let community_aggregates_before_delete =
|
|
||||||
CommunityAggregates::read(pool, inserted_community.id).await?;
|
|
||||||
|
|
||||||
assert_eq!(2, community_aggregates_before_delete.subscribers);
|
|
||||||
assert_eq!(2, community_aggregates_before_delete.subscribers_local);
|
|
||||||
assert_eq!(1, community_aggregates_before_delete.posts);
|
|
||||||
assert_eq!(2, community_aggregates_before_delete.comments);
|
|
||||||
|
|
||||||
// Test the other community
|
|
||||||
let another_community_aggs =
|
|
||||||
CommunityAggregates::read(pool, another_inserted_community.id).await?;
|
|
||||||
assert_eq!(1, another_community_aggs.subscribers);
|
|
||||||
assert_eq!(1, another_community_aggs.subscribers_local);
|
|
||||||
assert_eq!(0, another_community_aggs.posts);
|
|
||||||
assert_eq!(0, another_community_aggs.comments);
|
|
||||||
|
|
||||||
// Unfollow test
|
|
||||||
CommunityFollower::unfollow(pool, &second_person_follow).await?;
|
|
||||||
let after_unfollow = CommunityAggregates::read(pool, inserted_community.id).await?;
|
|
||||||
assert_eq!(1, after_unfollow.subscribers);
|
|
||||||
assert_eq!(1, after_unfollow.subscribers_local);
|
|
||||||
|
|
||||||
// Follow again just for the later tests
|
|
||||||
CommunityFollower::follow(pool, &second_person_follow).await?;
|
|
||||||
let after_follow_again = CommunityAggregates::read(pool, inserted_community.id).await?;
|
|
||||||
assert_eq!(2, after_follow_again.subscribers);
|
|
||||||
assert_eq!(2, after_follow_again.subscribers_local);
|
|
||||||
|
|
||||||
// Remove a parent post (the comment count should also be 0)
|
|
||||||
Post::delete(pool, inserted_post.id).await?;
|
|
||||||
let after_parent_post_delete = CommunityAggregates::read(pool, inserted_community.id).await?;
|
|
||||||
assert_eq!(0, after_parent_post_delete.comments);
|
|
||||||
assert_eq!(0, after_parent_post_delete.posts);
|
|
||||||
|
|
||||||
// Remove the 2nd person
|
|
||||||
Person::delete(pool, another_inserted_person.id).await?;
|
|
||||||
let after_person_delete = CommunityAggregates::read(pool, inserted_community.id).await?;
|
|
||||||
assert_eq!(1, after_person_delete.subscribers);
|
|
||||||
assert_eq!(1, after_person_delete.subscribers_local);
|
|
||||||
|
|
||||||
// This should delete all the associated rows, and fire triggers
|
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
|
||||||
assert_eq!(1, person_num_deleted);
|
|
||||||
|
|
||||||
// Delete the community
|
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
|
||||||
assert_eq!(1, community_num_deleted);
|
|
||||||
|
|
||||||
let another_community_num_deleted =
|
|
||||||
Community::delete(pool, another_inserted_community.id).await?;
|
|
||||||
assert_eq!(1, another_community_num_deleted);
|
|
||||||
|
|
||||||
// Should be none found, since the creator was deleted
|
|
||||||
let after_delete = CommunityAggregates::read(pool, inserted_community.id).await;
|
|
||||||
assert!(after_delete.is_err());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
#[cfg(feature = "full")]
|
|
||||||
pub mod comment_aggregates;
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
pub mod community_aggregates;
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
pub mod person_aggregates;
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
pub mod person_post_aggregates;
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
pub mod post_aggregates;
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
pub mod site_aggregates;
|
|
||||||
pub mod structs;
|
|
|
@ -1,182 +0,0 @@
|
||||||
use crate::{
|
|
||||||
aggregates::structs::PersonAggregates,
|
|
||||||
newtypes::PersonId,
|
|
||||||
schema::person_aggregates,
|
|
||||||
utils::{get_conn, DbPool},
|
|
||||||
};
|
|
||||||
use diesel::{result::Error, QueryDsl};
|
|
||||||
use diesel_async::RunQueryDsl;
|
|
||||||
|
|
||||||
impl PersonAggregates {
|
|
||||||
pub async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
person_aggregates::table.find(person_id).first(conn).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
aggregates::person_aggregates::PersonAggregates,
|
|
||||||
source::{
|
|
||||||
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
|
|
||||||
community::{Community, CommunityInsertForm},
|
|
||||||
instance::Instance,
|
|
||||||
person::{Person, PersonInsertForm},
|
|
||||||
post::{Post, PostInsertForm, PostLike, PostLikeForm},
|
|
||||||
},
|
|
||||||
traits::{Crud, Likeable},
|
|
||||||
utils::build_db_pool_for_tests,
|
|
||||||
};
|
|
||||||
use diesel::result::Error;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use serial_test::serial;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_crud() -> Result<(), Error> {
|
|
||||||
let pool = &build_db_pool_for_tests();
|
|
||||||
let pool = &mut pool.into();
|
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_user_agg");
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await?;
|
|
||||||
|
|
||||||
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_user_agg");
|
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await?;
|
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
|
||||||
inserted_instance.id,
|
|
||||||
"TIL_site_agg".into(),
|
|
||||||
"nada".to_owned(),
|
|
||||||
"pubkey".to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await?;
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
|
||||||
"A test post".into(),
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_community.id,
|
|
||||||
);
|
|
||||||
let inserted_post = Post::create(pool, &new_post).await?;
|
|
||||||
|
|
||||||
let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1);
|
|
||||||
let _inserted_post_like = PostLike::like(pool, &post_like).await?;
|
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
|
||||||
|
|
||||||
let mut comment_like = CommentLikeForm {
|
|
||||||
comment_id: inserted_comment.id,
|
|
||||||
person_id: inserted_person.id,
|
|
||||||
score: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
let _inserted_comment_like = CommentLike::like(pool, &comment_like).await?;
|
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let inserted_child_comment =
|
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
|
||||||
|
|
||||||
let child_comment_like = CommentLikeForm {
|
|
||||||
comment_id: inserted_child_comment.id,
|
|
||||||
person_id: another_inserted_person.id,
|
|
||||||
score: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
let _inserted_child_comment_like = CommentLike::like(pool, &child_comment_like).await?;
|
|
||||||
|
|
||||||
let person_aggregates_before_delete = PersonAggregates::read(pool, inserted_person.id).await?;
|
|
||||||
|
|
||||||
assert_eq!(1, person_aggregates_before_delete.post_count);
|
|
||||||
assert_eq!(1, person_aggregates_before_delete.post_score);
|
|
||||||
assert_eq!(2, person_aggregates_before_delete.comment_count);
|
|
||||||
assert_eq!(2, person_aggregates_before_delete.comment_score);
|
|
||||||
|
|
||||||
// Remove a post like
|
|
||||||
PostLike::remove(pool, inserted_person.id, inserted_post.id).await?;
|
|
||||||
let after_post_like_remove = PersonAggregates::read(pool, inserted_person.id).await?;
|
|
||||||
assert_eq!(0, after_post_like_remove.post_score);
|
|
||||||
|
|
||||||
Comment::update(
|
|
||||||
pool,
|
|
||||||
inserted_comment.id,
|
|
||||||
&CommentUpdateForm {
|
|
||||||
removed: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Comment::update(
|
|
||||||
pool,
|
|
||||||
inserted_child_comment.id,
|
|
||||||
&CommentUpdateForm {
|
|
||||||
removed: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let after_parent_comment_removed = PersonAggregates::read(pool, inserted_person.id).await?;
|
|
||||||
assert_eq!(0, after_parent_comment_removed.comment_count);
|
|
||||||
// TODO: fix person aggregate comment score calculation
|
|
||||||
// assert_eq!(0, after_parent_comment_removed.comment_score);
|
|
||||||
|
|
||||||
// Remove a parent comment (the scores should also be removed)
|
|
||||||
Comment::delete(pool, inserted_comment.id).await?;
|
|
||||||
Comment::delete(pool, inserted_child_comment.id).await?;
|
|
||||||
let after_parent_comment_delete = PersonAggregates::read(pool, inserted_person.id).await?;
|
|
||||||
assert_eq!(0, after_parent_comment_delete.comment_count);
|
|
||||||
// TODO: fix person aggregate comment score calculation
|
|
||||||
// assert_eq!(0, after_parent_comment_delete.comment_score);
|
|
||||||
|
|
||||||
// Add in the two comments again, then delete the post.
|
|
||||||
let new_parent_comment = Comment::create(pool, &comment_form, None).await?;
|
|
||||||
let _new_child_comment =
|
|
||||||
Comment::create(pool, &child_comment_form, Some(&new_parent_comment.path)).await?;
|
|
||||||
comment_like.comment_id = new_parent_comment.id;
|
|
||||||
CommentLike::like(pool, &comment_like).await?;
|
|
||||||
let after_comment_add = PersonAggregates::read(pool, inserted_person.id).await?;
|
|
||||||
assert_eq!(2, after_comment_add.comment_count);
|
|
||||||
// TODO: fix person aggregate comment score calculation
|
|
||||||
// assert_eq!(1, after_comment_add.comment_score);
|
|
||||||
|
|
||||||
Post::delete(pool, inserted_post.id).await?;
|
|
||||||
let after_post_delete = PersonAggregates::read(pool, inserted_person.id).await?;
|
|
||||||
// TODO: fix person aggregate comment score calculation
|
|
||||||
// assert_eq!(0, after_post_delete.comment_score);
|
|
||||||
assert_eq!(0, after_post_delete.comment_count);
|
|
||||||
assert_eq!(0, after_post_delete.post_score);
|
|
||||||
assert_eq!(0, after_post_delete.post_count);
|
|
||||||
|
|
||||||
// This should delete all the associated rows, and fire triggers
|
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
|
||||||
assert_eq!(1, person_num_deleted);
|
|
||||||
Person::delete(pool, another_inserted_person.id).await?;
|
|
||||||
|
|
||||||
// Delete the community
|
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
|
||||||
assert_eq!(1, community_num_deleted);
|
|
||||||
|
|
||||||
// Should be none found
|
|
||||||
let after_delete = PersonAggregates::read(pool, inserted_person.id).await;
|
|
||||||
assert!(after_delete.is_err());
|
|
||||||
|
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,270 +0,0 @@
|
||||||
use crate::{
|
|
||||||
aggregates::structs::PostAggregates,
|
|
||||||
newtypes::PostId,
|
|
||||||
schema::{community_aggregates, post, post_aggregates},
|
|
||||||
utils::{
|
|
||||||
functions::{hot_rank, scaled_rank},
|
|
||||||
get_conn,
|
|
||||||
DbPool,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl};
|
|
||||||
use diesel_async::RunQueryDsl;
|
|
||||||
|
|
||||||
impl PostAggregates {
|
|
||||||
pub async fn read(pool: &mut DbPool<'_>, post_id: PostId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
post_aggregates::table.find(post_id).first(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_ranks(pool: &mut DbPool<'_>, post_id: PostId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
|
|
||||||
// Diesel can't update based on a join, which is necessary for the scaled_rank
|
|
||||||
// https://github.com/diesel-rs/diesel/issues/1478
|
|
||||||
// Just select the metrics we need manually, for now, since its a single post anyway
|
|
||||||
|
|
||||||
let interactions_month = community_aggregates::table
|
|
||||||
.select(community_aggregates::interactions_month)
|
|
||||||
.inner_join(post::table.on(community_aggregates::community_id.eq(post::community_id)))
|
|
||||||
.filter(post::id.eq(post_id))
|
|
||||||
.first::<i64>(conn)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
diesel::update(post_aggregates::table.find(post_id))
|
|
||||||
.set((
|
|
||||||
post_aggregates::hot_rank.eq(hot_rank(post_aggregates::score, post_aggregates::published)),
|
|
||||||
post_aggregates::hot_rank_active.eq(hot_rank(
|
|
||||||
post_aggregates::score,
|
|
||||||
post_aggregates::newest_comment_time_necro,
|
|
||||||
)),
|
|
||||||
post_aggregates::scaled_rank.eq(scaled_rank(
|
|
||||||
post_aggregates::score,
|
|
||||||
post_aggregates::published,
|
|
||||||
interactions_month,
|
|
||||||
)),
|
|
||||||
))
|
|
||||||
.get_result::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
aggregates::post_aggregates::PostAggregates,
|
|
||||||
source::{
|
|
||||||
comment::{Comment, CommentInsertForm, CommentUpdateForm},
|
|
||||||
community::{Community, CommunityInsertForm},
|
|
||||||
instance::Instance,
|
|
||||||
person::{Person, PersonInsertForm},
|
|
||||||
post::{Post, PostInsertForm, PostLike, PostLikeForm},
|
|
||||||
},
|
|
||||||
traits::{Crud, Likeable},
|
|
||||||
utils::build_db_pool_for_tests,
|
|
||||||
};
|
|
||||||
use diesel::result::Error;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use serial_test::serial;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_crud() -> Result<(), Error> {
|
|
||||||
let pool = &build_db_pool_for_tests();
|
|
||||||
let pool = &mut pool.into();
|
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await?;
|
|
||||||
|
|
||||||
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await?;
|
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
|
||||||
inserted_instance.id,
|
|
||||||
"TIL_community_agg".into(),
|
|
||||||
"nada".to_owned(),
|
|
||||||
"pubkey".to_string(),
|
|
||||||
);
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await?;
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
|
||||||
"A test post".into(),
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_community.id,
|
|
||||||
);
|
|
||||||
let inserted_post = Post::create(pool, &new_post).await?;
|
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let inserted_child_comment =
|
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
|
||||||
|
|
||||||
let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1);
|
|
||||||
|
|
||||||
PostLike::like(pool, &post_like).await?;
|
|
||||||
|
|
||||||
let post_aggs_before_delete = PostAggregates::read(pool, inserted_post.id).await?;
|
|
||||||
|
|
||||||
assert_eq!(2, post_aggs_before_delete.comments);
|
|
||||||
assert_eq!(1, post_aggs_before_delete.score);
|
|
||||||
assert_eq!(1, post_aggs_before_delete.upvotes);
|
|
||||||
assert_eq!(0, post_aggs_before_delete.downvotes);
|
|
||||||
|
|
||||||
// Add a post dislike from the other person
|
|
||||||
let post_dislike = PostLikeForm::new(inserted_post.id, another_inserted_person.id, -1);
|
|
||||||
|
|
||||||
PostLike::like(pool, &post_dislike).await?;
|
|
||||||
|
|
||||||
let post_aggs_after_dislike = PostAggregates::read(pool, inserted_post.id).await?;
|
|
||||||
|
|
||||||
assert_eq!(2, post_aggs_after_dislike.comments);
|
|
||||||
assert_eq!(0, post_aggs_after_dislike.score);
|
|
||||||
assert_eq!(1, post_aggs_after_dislike.upvotes);
|
|
||||||
assert_eq!(1, post_aggs_after_dislike.downvotes);
|
|
||||||
|
|
||||||
// Remove the comments
|
|
||||||
Comment::delete(pool, inserted_comment.id).await?;
|
|
||||||
Comment::delete(pool, inserted_child_comment.id).await?;
|
|
||||||
let after_comment_delete = PostAggregates::read(pool, inserted_post.id).await?;
|
|
||||||
assert_eq!(0, after_comment_delete.comments);
|
|
||||||
assert_eq!(0, after_comment_delete.score);
|
|
||||||
assert_eq!(1, after_comment_delete.upvotes);
|
|
||||||
assert_eq!(1, after_comment_delete.downvotes);
|
|
||||||
|
|
||||||
// Remove the first post like
|
|
||||||
PostLike::remove(pool, inserted_person.id, inserted_post.id).await?;
|
|
||||||
let after_like_remove = PostAggregates::read(pool, inserted_post.id).await?;
|
|
||||||
assert_eq!(0, after_like_remove.comments);
|
|
||||||
assert_eq!(-1, after_like_remove.score);
|
|
||||||
assert_eq!(0, after_like_remove.upvotes);
|
|
||||||
assert_eq!(1, after_like_remove.downvotes);
|
|
||||||
|
|
||||||
// This should delete all the associated rows, and fire triggers
|
|
||||||
Person::delete(pool, another_inserted_person.id).await?;
|
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
|
||||||
assert_eq!(1, person_num_deleted);
|
|
||||||
|
|
||||||
// Delete the community
|
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
|
||||||
assert_eq!(1, community_num_deleted);
|
|
||||||
|
|
||||||
// Should be none found, since the creator was deleted
|
|
||||||
let after_delete = PostAggregates::read(pool, inserted_post.id).await;
|
|
||||||
assert!(after_delete.is_err());
|
|
||||||
|
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_soft_delete() -> Result<(), Error> {
|
|
||||||
let pool = &build_db_pool_for_tests();
|
|
||||||
let pool = &mut pool.into();
|
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await?;
|
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
|
||||||
inserted_instance.id,
|
|
||||||
"TIL_community_agg".into(),
|
|
||||||
"nada".to_owned(),
|
|
||||||
"pubkey".to_string(),
|
|
||||||
);
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await?;
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
|
||||||
"A test post".into(),
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_community.id,
|
|
||||||
);
|
|
||||||
let inserted_post = Post::create(pool, &new_post).await?;
|
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
|
||||||
|
|
||||||
let post_aggregates_before = PostAggregates::read(pool, inserted_post.id).await?;
|
|
||||||
assert_eq!(1, post_aggregates_before.comments);
|
|
||||||
|
|
||||||
Comment::update(
|
|
||||||
pool,
|
|
||||||
inserted_comment.id,
|
|
||||||
&CommentUpdateForm {
|
|
||||||
removed: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let post_aggregates_after_remove = PostAggregates::read(pool, inserted_post.id).await?;
|
|
||||||
assert_eq!(0, post_aggregates_after_remove.comments);
|
|
||||||
|
|
||||||
Comment::update(
|
|
||||||
pool,
|
|
||||||
inserted_comment.id,
|
|
||||||
&CommentUpdateForm {
|
|
||||||
removed: Some(false),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Comment::update(
|
|
||||||
pool,
|
|
||||||
inserted_comment.id,
|
|
||||||
&CommentUpdateForm {
|
|
||||||
deleted: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let post_aggregates_after_delete = PostAggregates::read(pool, inserted_post.id).await?;
|
|
||||||
assert_eq!(0, post_aggregates_after_delete.comments);
|
|
||||||
|
|
||||||
Comment::update(
|
|
||||||
pool,
|
|
||||||
inserted_comment.id,
|
|
||||||
&CommentUpdateForm {
|
|
||||||
removed: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let post_aggregates_after_delete_remove = PostAggregates::read(pool, inserted_post.id).await?;
|
|
||||||
assert_eq!(0, post_aggregates_after_delete_remove.comments);
|
|
||||||
|
|
||||||
Comment::delete(pool, inserted_comment.id).await?;
|
|
||||||
Post::delete(pool, inserted_post.id).await?;
|
|
||||||
Person::delete(pool, inserted_person.id).await?;
|
|
||||||
Community::delete(pool, inserted_community.id).await?;
|
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,204 +0,0 @@
|
||||||
use crate::{
|
|
||||||
aggregates::structs::SiteAggregates,
|
|
||||||
schema::site_aggregates,
|
|
||||||
utils::{get_conn, DbPool},
|
|
||||||
};
|
|
||||||
use diesel::result::Error;
|
|
||||||
use diesel_async::RunQueryDsl;
|
|
||||||
|
|
||||||
impl SiteAggregates {
|
|
||||||
pub async fn read(pool: &mut DbPool<'_>) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
site_aggregates::table.first(conn).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
aggregates::site_aggregates::SiteAggregates,
|
|
||||||
source::{
|
|
||||||
comment::{Comment, CommentInsertForm},
|
|
||||||
community::{Community, CommunityInsertForm, CommunityUpdateForm},
|
|
||||||
instance::Instance,
|
|
||||||
person::{Person, PersonInsertForm},
|
|
||||||
post::{Post, PostInsertForm},
|
|
||||||
site::{Site, SiteInsertForm},
|
|
||||||
},
|
|
||||||
traits::Crud,
|
|
||||||
utils::{build_db_pool_for_tests, DbPool},
|
|
||||||
};
|
|
||||||
use diesel::result::Error;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use serial_test::serial;
|
|
||||||
|
|
||||||
async fn prepare_site_with_community(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
) -> Result<(Instance, Person, Site, Community), Error> {
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_site_agg");
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await?;
|
|
||||||
|
|
||||||
let site_form = SiteInsertForm::new("test_site".into(), inserted_instance.id);
|
|
||||||
let inserted_site = Site::create(pool, &site_form).await?;
|
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
|
||||||
inserted_instance.id,
|
|
||||||
"TIL_site_agg".into(),
|
|
||||||
"nada".to_owned(),
|
|
||||||
"pubkey".to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
inserted_instance,
|
|
||||||
inserted_person,
|
|
||||||
inserted_site,
|
|
||||||
inserted_community,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_crud() -> Result<(), Error> {
|
|
||||||
let pool = &build_db_pool_for_tests();
|
|
||||||
let pool = &mut pool.into();
|
|
||||||
|
|
||||||
let (inserted_instance, inserted_person, inserted_site, inserted_community) =
|
|
||||||
prepare_site_with_community(pool).await?;
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
|
||||||
"A test post".into(),
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_community.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Insert two of those posts
|
|
||||||
let inserted_post = Post::create(pool, &new_post).await?;
|
|
||||||
let _inserted_post_again = Post::create(pool, &new_post).await?;
|
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Insert two of those comments
|
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let _inserted_child_comment =
|
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
|
||||||
|
|
||||||
let site_aggregates_before_delete = SiteAggregates::read(pool).await?;
|
|
||||||
|
|
||||||
// TODO: this is unstable, sometimes it returns 0 users, sometimes 1
|
|
||||||
//assert_eq!(0, site_aggregates_before_delete.users);
|
|
||||||
assert_eq!(1, site_aggregates_before_delete.communities);
|
|
||||||
assert_eq!(2, site_aggregates_before_delete.posts);
|
|
||||||
assert_eq!(2, site_aggregates_before_delete.comments);
|
|
||||||
|
|
||||||
// Try a post delete
|
|
||||||
Post::delete(pool, inserted_post.id).await?;
|
|
||||||
let site_aggregates_after_post_delete = SiteAggregates::read(pool).await?;
|
|
||||||
assert_eq!(1, site_aggregates_after_post_delete.posts);
|
|
||||||
assert_eq!(0, site_aggregates_after_post_delete.comments);
|
|
||||||
|
|
||||||
// This shouuld delete all the associated rows, and fire triggers
|
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
|
||||||
assert_eq!(1, person_num_deleted);
|
|
||||||
|
|
||||||
// Delete the community
|
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
|
||||||
assert_eq!(1, community_num_deleted);
|
|
||||||
|
|
||||||
// Site should still exist, it can without a site creator.
|
|
||||||
let after_delete_creator = SiteAggregates::read(pool).await;
|
|
||||||
assert!(after_delete_creator.is_ok());
|
|
||||||
|
|
||||||
Site::delete(pool, inserted_site.id).await?;
|
|
||||||
let after_delete_site = SiteAggregates::read(pool).await;
|
|
||||||
assert!(after_delete_site.is_err());
|
|
||||||
|
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_soft_delete() -> Result<(), Error> {
|
|
||||||
let pool = &build_db_pool_for_tests();
|
|
||||||
let pool = &mut pool.into();
|
|
||||||
|
|
||||||
let (inserted_instance, inserted_person, inserted_site, inserted_community) =
|
|
||||||
prepare_site_with_community(pool).await?;
|
|
||||||
|
|
||||||
let site_aggregates_before = SiteAggregates::read(pool).await?;
|
|
||||||
assert_eq!(1, site_aggregates_before.communities);
|
|
||||||
|
|
||||||
Community::update(
|
|
||||||
pool,
|
|
||||||
inserted_community.id,
|
|
||||||
&CommunityUpdateForm {
|
|
||||||
deleted: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let site_aggregates_after_delete = SiteAggregates::read(pool).await?;
|
|
||||||
assert_eq!(0, site_aggregates_after_delete.communities);
|
|
||||||
|
|
||||||
Community::update(
|
|
||||||
pool,
|
|
||||||
inserted_community.id,
|
|
||||||
&CommunityUpdateForm {
|
|
||||||
deleted: Some(false),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Community::update(
|
|
||||||
pool,
|
|
||||||
inserted_community.id,
|
|
||||||
&CommunityUpdateForm {
|
|
||||||
removed: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let site_aggregates_after_remove = SiteAggregates::read(pool).await?;
|
|
||||||
assert_eq!(0, site_aggregates_after_remove.communities);
|
|
||||||
|
|
||||||
Community::update(
|
|
||||||
pool,
|
|
||||||
inserted_community.id,
|
|
||||||
&CommunityUpdateForm {
|
|
||||||
deleted: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let site_aggregates_after_remove_delete = SiteAggregates::read(pool).await?;
|
|
||||||
assert_eq!(0, site_aggregates_after_remove_delete.communities);
|
|
||||||
|
|
||||||
Community::delete(pool, inserted_community.id).await?;
|
|
||||||
Site::delete(pool, inserted_site.id).await?;
|
|
||||||
Person::delete(pool, inserted_person.id).await?;
|
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,220 +0,0 @@
|
||||||
use crate::newtypes::{CommentId, CommunityId, InstanceId, PersonId, PostId, SiteId};
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use crate::schema::{
|
|
||||||
comment_aggregates,
|
|
||||||
community_aggregates,
|
|
||||||
person_aggregates,
|
|
||||||
post_actions,
|
|
||||||
post_aggregates,
|
|
||||||
site_aggregates,
|
|
||||||
};
|
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use diesel::{dsl, expression_methods::NullableExpressionMethods};
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use i_love_jesus::CursorKeysModule;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use ts_rs::TS;
|
|
||||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "full",
|
|
||||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = comment_aggregates))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(primary_key(comment_id)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// Aggregate data for a comment.
|
|
||||||
pub struct CommentAggregates {
|
|
||||||
pub comment_id: CommentId,
|
|
||||||
pub score: i64,
|
|
||||||
pub upvotes: i64,
|
|
||||||
pub downvotes: i64,
|
|
||||||
pub published: DateTime<Utc>,
|
|
||||||
/// The total number of children in this comment branch.
|
|
||||||
pub child_count: i32,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub hot_rank: f64,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub controversy_rank: f64,
|
|
||||||
pub report_count: i16,
|
|
||||||
pub unresolved_report_count: i16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "full",
|
|
||||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = community_aggregates))]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "full",
|
|
||||||
diesel(belongs_to(crate::source::community::Community))
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "full", diesel(primary_key(community_id)))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// Aggregate data for a community.
|
|
||||||
pub struct CommunityAggregates {
|
|
||||||
pub community_id: CommunityId,
|
|
||||||
pub subscribers: i64,
|
|
||||||
pub posts: i64,
|
|
||||||
pub comments: i64,
|
|
||||||
pub published: DateTime<Utc>,
|
|
||||||
/// The number of users with any activity in the last day.
|
|
||||||
pub users_active_day: i64,
|
|
||||||
/// The number of users with any activity in the last week.
|
|
||||||
pub users_active_week: i64,
|
|
||||||
/// The number of users with any activity in the last month.
|
|
||||||
pub users_active_month: i64,
|
|
||||||
/// The number of users with any activity in the last year.
|
|
||||||
pub users_active_half_year: i64,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub hot_rank: f64,
|
|
||||||
pub subscribers_local: i64,
|
|
||||||
pub report_count: i16,
|
|
||||||
pub unresolved_report_count: i16,
|
|
||||||
/// Number of any interactions over the last month.
|
|
||||||
#[serde(skip)]
|
|
||||||
pub interactions_month: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "full",
|
|
||||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = person_aggregates))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// Aggregate data for a person.
|
|
||||||
pub struct PersonAggregates {
|
|
||||||
pub person_id: PersonId,
|
|
||||||
pub post_count: i64,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub post_score: i64,
|
|
||||||
pub comment_count: i64,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub comment_score: i64,
|
|
||||||
pub published: DateTime<Utc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "full",
|
|
||||||
derive(
|
|
||||||
Queryable,
|
|
||||||
Selectable,
|
|
||||||
Associations,
|
|
||||||
Identifiable,
|
|
||||||
TS,
|
|
||||||
CursorKeysModule
|
|
||||||
)
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = post_aggregates))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(primary_key(post_id)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
#[cfg_attr(feature = "full", cursor_keys_module(name = post_aggregates_keys))]
|
|
||||||
/// Aggregate data for a post.
|
|
||||||
pub struct PostAggregates {
|
|
||||||
pub post_id: PostId,
|
|
||||||
pub comments: i64,
|
|
||||||
pub score: i64,
|
|
||||||
pub upvotes: i64,
|
|
||||||
pub downvotes: i64,
|
|
||||||
pub published: DateTime<Utc>,
|
|
||||||
#[serde(skip)]
|
|
||||||
/// A newest comment time, limited to 2 days, to prevent necrobumping
|
|
||||||
pub newest_comment_time_necro: DateTime<Utc>,
|
|
||||||
/// The time of the newest comment in the post.
|
|
||||||
pub newest_comment_time: DateTime<Utc>,
|
|
||||||
/// If the post is featured on the community.
|
|
||||||
#[serde(skip)]
|
|
||||||
pub featured_community: bool,
|
|
||||||
/// If the post is featured on the site / to local.
|
|
||||||
#[serde(skip)]
|
|
||||||
pub featured_local: bool,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub hot_rank: f64,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub hot_rank_active: f64,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub community_id: CommunityId,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub creator_id: PersonId,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub controversy_rank: f64,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub instance_id: InstanceId,
|
|
||||||
/// A rank that amplifies smaller communities
|
|
||||||
#[serde(skip)]
|
|
||||||
pub scaled_rank: f64,
|
|
||||||
pub report_count: i16,
|
|
||||||
pub unresolved_report_count: i16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "full",
|
|
||||||
derive(Queryable, Selectable, Associations, Identifiable)
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = post_actions))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
|
||||||
/// Aggregate data for a person's post.
|
|
||||||
pub struct PersonPostAggregates {
|
|
||||||
pub person_id: PersonId,
|
|
||||||
pub post_id: PostId,
|
|
||||||
/// The number of comments they've read on that post.
|
|
||||||
///
|
|
||||||
/// This is updated to the current post comment count every time they view a post.
|
|
||||||
#[cfg_attr(feature = "full", diesel(select_expression = post_actions::read_comments_amount.assume_not_null()))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<post_actions::read_comments_amount>))]
|
|
||||||
pub read_comments: i64,
|
|
||||||
#[cfg_attr(feature = "full", diesel(select_expression = post_actions::read_comments.assume_not_null()))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<post_actions::read_comments>))]
|
|
||||||
pub published: DateTime<Utc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = post_actions))]
|
|
||||||
pub struct PersonPostAggregatesForm {
|
|
||||||
pub person_id: PersonId,
|
|
||||||
pub post_id: PostId,
|
|
||||||
#[cfg_attr(feature = "full", diesel(column_name = read_comments_amount))]
|
|
||||||
pub read_comments: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy, Hash)]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "full",
|
|
||||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = site_aggregates))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::site::Site)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(primary_key(site_id)))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// Aggregate data for a site.
|
|
||||||
pub struct SiteAggregates {
|
|
||||||
pub site_id: SiteId,
|
|
||||||
pub users: i64,
|
|
||||||
pub posts: i64,
|
|
||||||
pub comments: i64,
|
|
||||||
pub communities: i64,
|
|
||||||
/// The number of users with any activity in the last day.
|
|
||||||
pub users_active_day: i64,
|
|
||||||
/// The number of users with any activity in the last week.
|
|
||||||
pub users_active_week: i64,
|
|
||||||
/// The number of users with any activity in the last month.
|
|
||||||
pub users_active_month: i64,
|
|
||||||
/// The number of users with any activity in the last half year.
|
|
||||||
pub users_active_half_year: i64,
|
|
||||||
}
|
|
|
@ -13,7 +13,14 @@ use crate::{
|
||||||
CommentUpdateForm,
|
CommentUpdateForm,
|
||||||
},
|
},
|
||||||
traits::{Crud, Likeable, Saveable},
|
traits::{Crud, Likeable, Saveable},
|
||||||
utils::{functions::coalesce, get_conn, now, uplete, DbPool, DELETED_REPLACEMENT_TEXT},
|
utils::{
|
||||||
|
functions::{coalesce, hot_rank},
|
||||||
|
get_conn,
|
||||||
|
now,
|
||||||
|
uplete,
|
||||||
|
DbPool,
|
||||||
|
DELETED_REPLACEMENT_TEXT,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
|
@ -119,7 +126,17 @@ impl Comment {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub async fn update_hot_rank(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
comment_id: CommentId,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
|
diesel::update(comment::table.find(comment_id))
|
||||||
|
.set(comment::hot_rank.eq(hot_rank(comment::score, comment::published)))
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
pub fn local_url(&self, settings: &Settings) -> LemmyResult<DbUrl> {
|
pub fn local_url(&self, settings: &Settings) -> LemmyResult<DbUrl> {
|
||||||
let domain = settings.get_protocol_and_hostname();
|
let domain = settings.get_protocol_and_hostname();
|
||||||
Ok(Url::parse(&format!("{domain}/comment/{}", self.id))?.into())
|
Ok(Url::parse(&format!("{domain}/comment/{}", self.id))?.into())
|
||||||
|
@ -150,6 +167,14 @@ pub fn comment_select_remove_deletes() -> _ {
|
||||||
comment::path,
|
comment::path,
|
||||||
comment::distinguished,
|
comment::distinguished,
|
||||||
comment::language_id,
|
comment::language_id,
|
||||||
|
comment::score,
|
||||||
|
comment::upvotes,
|
||||||
|
comment::downvotes,
|
||||||
|
comment::child_count,
|
||||||
|
comment::hot_rank,
|
||||||
|
comment::controversy_rank,
|
||||||
|
comment::report_count,
|
||||||
|
comment::unresolved_report_count,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,25 +267,17 @@ impl Saveable for CommentSaved {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::LanguageId,
|
newtypes::LanguageId,
|
||||||
source::{
|
source::{
|
||||||
comment::{
|
|
||||||
Comment,
|
|
||||||
CommentInsertForm,
|
|
||||||
CommentLike,
|
|
||||||
CommentLikeForm,
|
|
||||||
CommentSaved,
|
|
||||||
CommentSavedForm,
|
|
||||||
CommentUpdateForm,
|
|
||||||
},
|
|
||||||
community::{Community, CommunityInsertForm},
|
community::{Community, CommunityInsertForm},
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
post::{Post, PostInsertForm},
|
post::{Post, PostInsertForm},
|
||||||
},
|
},
|
||||||
traits::{Crud, Likeable, Saveable},
|
traits::{Crud, Likeable, Saveable},
|
||||||
utils::{build_db_pool_for_tests, uplete},
|
utils::{build_db_pool_for_tests, uplete, RANK_DEFAULT},
|
||||||
};
|
};
|
||||||
use diesel_ltree::Ltree;
|
use diesel_ltree::Ltree;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
@ -320,6 +337,14 @@ mod tests {
|
||||||
distinguished: false,
|
distinguished: false,
|
||||||
local: true,
|
local: true,
|
||||||
language_id: LanguageId::default(),
|
language_id: LanguageId::default(),
|
||||||
|
child_count: 1,
|
||||||
|
controversy_rank: 0.0,
|
||||||
|
downvotes: 0,
|
||||||
|
upvotes: 1,
|
||||||
|
score: 1,
|
||||||
|
hot_rank: RANK_DEFAULT,
|
||||||
|
report_count: 0,
|
||||||
|
unresolved_report_count: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
let child_comment_form = CommentInsertForm::new(
|
||||||
|
@ -374,7 +399,6 @@ mod tests {
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
assert_eq!(expected_comment, read_comment);
|
assert_eq!(expected_comment, read_comment);
|
||||||
assert_eq!(expected_comment, inserted_comment);
|
|
||||||
assert_eq!(expected_comment, updated_comment);
|
assert_eq!(expected_comment, updated_comment);
|
||||||
assert_eq!(expected_comment_like, inserted_comment_like);
|
assert_eq!(expected_comment_like, inserted_comment_like);
|
||||||
assert_eq!(expected_comment_saved, inserted_comment_saved);
|
assert_eq!(expected_comment_saved, inserted_comment_saved);
|
||||||
|
@ -388,4 +412,107 @@ mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_aggregates() -> Result<(), Error> {
|
||||||
|
let pool = &build_db_pool_for_tests();
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_comment_agg");
|
||||||
|
|
||||||
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_comment_agg");
|
||||||
|
|
||||||
|
let another_inserted_person = Person::create(pool, &another_person).await?;
|
||||||
|
|
||||||
|
let new_community = CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"TIL_comment_agg".into(),
|
||||||
|
"nada".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
);
|
||||||
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
|
let new_post = PostInsertForm::new(
|
||||||
|
"A test post".into(),
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_community.id,
|
||||||
|
);
|
||||||
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
|
let comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
|
let child_comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
let _inserted_child_comment =
|
||||||
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
|
|
||||||
|
let comment_like = CommentLikeForm {
|
||||||
|
comment_id: inserted_comment.id,
|
||||||
|
person_id: inserted_person.id,
|
||||||
|
score: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
CommentLike::like(pool, &comment_like).await?;
|
||||||
|
|
||||||
|
let comment_aggs_before_delete = Comment::read(pool, inserted_comment.id).await?;
|
||||||
|
|
||||||
|
assert_eq!(1, comment_aggs_before_delete.score);
|
||||||
|
assert_eq!(1, comment_aggs_before_delete.upvotes);
|
||||||
|
assert_eq!(0, comment_aggs_before_delete.downvotes);
|
||||||
|
|
||||||
|
// Add a post dislike from the other person
|
||||||
|
let comment_dislike = CommentLikeForm {
|
||||||
|
comment_id: inserted_comment.id,
|
||||||
|
person_id: another_inserted_person.id,
|
||||||
|
score: -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
CommentLike::like(pool, &comment_dislike).await?;
|
||||||
|
|
||||||
|
let comment_aggs_after_dislike = Comment::read(pool, inserted_comment.id).await?;
|
||||||
|
|
||||||
|
assert_eq!(0, comment_aggs_after_dislike.score);
|
||||||
|
assert_eq!(1, comment_aggs_after_dislike.upvotes);
|
||||||
|
assert_eq!(1, comment_aggs_after_dislike.downvotes);
|
||||||
|
|
||||||
|
// Remove the first comment like
|
||||||
|
CommentLike::remove(pool, inserted_person.id, inserted_comment.id).await?;
|
||||||
|
let after_like_remove = Comment::read(pool, inserted_comment.id).await?;
|
||||||
|
assert_eq!(-1, after_like_remove.score);
|
||||||
|
assert_eq!(0, after_like_remove.upvotes);
|
||||||
|
assert_eq!(1, after_like_remove.downvotes);
|
||||||
|
|
||||||
|
// Remove the parent post
|
||||||
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
|
|
||||||
|
// Should be none found, since the post was deleted
|
||||||
|
let after_delete = Comment::read(pool, inserted_comment.id).await;
|
||||||
|
assert!(after_delete.is_err());
|
||||||
|
|
||||||
|
// This should delete all the associated rows, and fire triggers
|
||||||
|
Person::delete(pool, another_inserted_person.id).await?;
|
||||||
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
|
assert_eq!(1, person_num_deleted);
|
||||||
|
|
||||||
|
// Delete the community
|
||||||
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,6 +280,19 @@ impl Community {
|
||||||
let domain = settings.get_protocol_and_hostname();
|
let domain = settings.get_protocol_and_hostname();
|
||||||
Ok(Url::parse(&format!("{domain}/c/{name}"))?.into())
|
Ok(Url::parse(&format!("{domain}/c/{name}"))?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn update_federated_followers(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
for_community_id: CommunityId,
|
||||||
|
new_subscribers: i32,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
let new_subscribers: i64 = new_subscribers.into();
|
||||||
|
diesel::update(community::table.find(for_community_id))
|
||||||
|
.set(community::dsl::subscribers.eq(new_subscribers))
|
||||||
|
.get_result(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommunityModerator {
|
impl CommunityModerator {
|
||||||
|
@ -551,6 +564,7 @@ impl ApubActor for Community {
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
source::{
|
source::{
|
||||||
|
comment::{Comment, CommentInsertForm},
|
||||||
community::{
|
community::{
|
||||||
Community,
|
Community,
|
||||||
CommunityFollower,
|
CommunityFollower,
|
||||||
|
@ -566,9 +580,10 @@ mod tests {
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
local_user::LocalUser,
|
local_user::LocalUser,
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
|
post::{Post, PostInsertForm},
|
||||||
},
|
},
|
||||||
traits::{Bannable, Crud, Followable, Joinable},
|
traits::{Bannable, Crud, Followable, Joinable},
|
||||||
utils::{build_db_pool_for_tests, uplete},
|
utils::{build_db_pool_for_tests, uplete, RANK_DEFAULT},
|
||||||
CommunityVisibility,
|
CommunityVisibility,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
@ -624,6 +639,18 @@ mod tests {
|
||||||
instance_id: inserted_instance.id,
|
instance_id: inserted_instance.id,
|
||||||
visibility: CommunityVisibility::Public,
|
visibility: CommunityVisibility::Public,
|
||||||
random_number: inserted_community.random_number,
|
random_number: inserted_community.random_number,
|
||||||
|
subscribers: 1,
|
||||||
|
posts: 0,
|
||||||
|
comments: 0,
|
||||||
|
users_active_day: 0,
|
||||||
|
users_active_week: 0,
|
||||||
|
users_active_month: 0,
|
||||||
|
users_active_half_year: 0,
|
||||||
|
hot_rank: RANK_DEFAULT,
|
||||||
|
subscribers_local: 1,
|
||||||
|
report_count: 0,
|
||||||
|
unresolved_report_count: 0,
|
||||||
|
interactions_month: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
|
@ -731,7 +758,6 @@ mod tests {
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
assert_eq!(expected_community, read_community);
|
assert_eq!(expected_community, read_community);
|
||||||
assert_eq!(expected_community, inserted_community);
|
|
||||||
assert_eq!(expected_community, updated_community);
|
assert_eq!(expected_community, updated_community);
|
||||||
assert_eq!(expected_community_follower, inserted_community_follower);
|
assert_eq!(expected_community_follower, inserted_community_follower);
|
||||||
assert_eq!(expected_community_moderator, inserted_bobby_moderator);
|
assert_eq!(expected_community_moderator, inserted_bobby_moderator);
|
||||||
|
@ -744,4 +770,142 @@ mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_aggregates() -> LemmyResult<()> {
|
||||||
|
let pool = &build_db_pool_for_tests();
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
||||||
|
|
||||||
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
||||||
|
|
||||||
|
let another_inserted_person = Person::create(pool, &another_person).await?;
|
||||||
|
|
||||||
|
let new_community = CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"TIL_community_agg".into(),
|
||||||
|
"nada".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
);
|
||||||
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
|
let another_community = CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"TIL_community_agg_2".into(),
|
||||||
|
"nada".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
);
|
||||||
|
let another_inserted_community = Community::create(pool, &another_community).await?;
|
||||||
|
|
||||||
|
let first_person_follow = CommunityFollowerForm {
|
||||||
|
community_id: inserted_community.id,
|
||||||
|
person_id: inserted_person.id,
|
||||||
|
state: Some(CommunityFollowerState::Accepted),
|
||||||
|
approver_id: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
CommunityFollower::follow(pool, &first_person_follow).await?;
|
||||||
|
|
||||||
|
let second_person_follow = CommunityFollowerForm {
|
||||||
|
community_id: inserted_community.id,
|
||||||
|
person_id: another_inserted_person.id,
|
||||||
|
state: Some(CommunityFollowerState::Accepted),
|
||||||
|
approver_id: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
CommunityFollower::follow(pool, &second_person_follow).await?;
|
||||||
|
|
||||||
|
let another_community_follow = CommunityFollowerForm {
|
||||||
|
community_id: another_inserted_community.id,
|
||||||
|
person_id: inserted_person.id,
|
||||||
|
state: Some(CommunityFollowerState::Accepted),
|
||||||
|
approver_id: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
CommunityFollower::follow(pool, &another_community_follow).await?;
|
||||||
|
|
||||||
|
let new_post = PostInsertForm::new(
|
||||||
|
"A test post".into(),
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_community.id,
|
||||||
|
);
|
||||||
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
|
let comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
|
let child_comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
let _inserted_child_comment =
|
||||||
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
|
|
||||||
|
let community_aggregates_before_delete = Community::read(pool, inserted_community.id).await?;
|
||||||
|
|
||||||
|
assert_eq!(2, community_aggregates_before_delete.subscribers);
|
||||||
|
assert_eq!(2, community_aggregates_before_delete.subscribers_local);
|
||||||
|
assert_eq!(1, community_aggregates_before_delete.posts);
|
||||||
|
assert_eq!(2, community_aggregates_before_delete.comments);
|
||||||
|
|
||||||
|
// Test the other community
|
||||||
|
let another_community_aggs = Community::read(pool, another_inserted_community.id).await?;
|
||||||
|
assert_eq!(1, another_community_aggs.subscribers);
|
||||||
|
assert_eq!(1, another_community_aggs.subscribers_local);
|
||||||
|
assert_eq!(0, another_community_aggs.posts);
|
||||||
|
assert_eq!(0, another_community_aggs.comments);
|
||||||
|
|
||||||
|
// Unfollow test
|
||||||
|
CommunityFollower::unfollow(pool, &second_person_follow).await?;
|
||||||
|
let after_unfollow = Community::read(pool, inserted_community.id).await?;
|
||||||
|
assert_eq!(1, after_unfollow.subscribers);
|
||||||
|
assert_eq!(1, after_unfollow.subscribers_local);
|
||||||
|
|
||||||
|
// Follow again just for the later tests
|
||||||
|
CommunityFollower::follow(pool, &second_person_follow).await?;
|
||||||
|
let after_follow_again = Community::read(pool, inserted_community.id).await?;
|
||||||
|
assert_eq!(2, after_follow_again.subscribers);
|
||||||
|
assert_eq!(2, after_follow_again.subscribers_local);
|
||||||
|
|
||||||
|
// Remove a parent post (the comment count should also be 0)
|
||||||
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
|
let after_parent_post_delete = Community::read(pool, inserted_community.id).await?;
|
||||||
|
assert_eq!(0, after_parent_post_delete.posts);
|
||||||
|
assert_eq!(0, after_parent_post_delete.comments);
|
||||||
|
|
||||||
|
// Remove the 2nd person
|
||||||
|
Person::delete(pool, another_inserted_person.id).await?;
|
||||||
|
let after_person_delete = Community::read(pool, inserted_community.id).await?;
|
||||||
|
assert_eq!(1, after_person_delete.subscribers);
|
||||||
|
assert_eq!(1, after_person_delete.subscribers_local);
|
||||||
|
|
||||||
|
// This should delete all the associated rows, and fire triggers
|
||||||
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
|
assert_eq!(1, person_num_deleted);
|
||||||
|
|
||||||
|
// Delete the community
|
||||||
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
|
let another_community_num_deleted =
|
||||||
|
Community::delete(pool, another_inserted_community.id).await?;
|
||||||
|
assert_eq!(1, another_community_num_deleted);
|
||||||
|
|
||||||
|
// Should be none found, since the creator was deleted
|
||||||
|
let after_delete = Community::read(pool, inserted_community.id).await;
|
||||||
|
assert!(after_delete.is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,3 +39,195 @@ impl LocalSite {
|
||||||
diesel::delete(local_site::table).execute(conn).await
|
diesel::delete(local_site::table).execute(conn).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::{
|
||||||
|
source::{
|
||||||
|
comment::{Comment, CommentInsertForm},
|
||||||
|
community::{Community, CommunityInsertForm, CommunityUpdateForm},
|
||||||
|
instance::Instance,
|
||||||
|
person::{Person, PersonInsertForm},
|
||||||
|
post::{Post, PostInsertForm},
|
||||||
|
site::{Site, SiteInsertForm},
|
||||||
|
},
|
||||||
|
traits::Crud,
|
||||||
|
utils::{build_db_pool_for_tests, DbPool},
|
||||||
|
};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
|
async fn prepare_site_with_community(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
) -> LemmyResult<(Instance, Person, Site, Community)> {
|
||||||
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_site_agg");
|
||||||
|
|
||||||
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
|
let site_form = SiteInsertForm::new("test_site".into(), inserted_instance.id);
|
||||||
|
let inserted_site = Site::create(pool, &site_form).await?;
|
||||||
|
|
||||||
|
let local_site_form = LocalSiteInsertForm::new(inserted_site.id);
|
||||||
|
LocalSite::create(pool, &local_site_form).await?;
|
||||||
|
|
||||||
|
let new_community = CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"TIL_site_agg".into(),
|
||||||
|
"nada".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
inserted_instance,
|
||||||
|
inserted_person,
|
||||||
|
inserted_site,
|
||||||
|
inserted_community,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_aggregates() -> LemmyResult<()> {
|
||||||
|
let pool = &build_db_pool_for_tests();
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
|
let (inserted_instance, inserted_person, inserted_site, inserted_community) =
|
||||||
|
prepare_site_with_community(pool).await?;
|
||||||
|
|
||||||
|
let new_post = PostInsertForm::new(
|
||||||
|
"A test post".into(),
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_community.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert two of those posts
|
||||||
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
let _inserted_post_again = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
|
let comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert two of those comments
|
||||||
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
|
let child_comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
let _inserted_child_comment =
|
||||||
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
|
|
||||||
|
let site_aggregates_before_delete = LocalSite::read(pool).await?;
|
||||||
|
|
||||||
|
// TODO: this is unstable, sometimes it returns 0 users, sometimes 1
|
||||||
|
//assert_eq!(0, site_aggregates_before_delete.users);
|
||||||
|
assert_eq!(1, site_aggregates_before_delete.communities);
|
||||||
|
assert_eq!(2, site_aggregates_before_delete.posts);
|
||||||
|
assert_eq!(2, site_aggregates_before_delete.comments);
|
||||||
|
|
||||||
|
// Try a post delete
|
||||||
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
|
let site_aggregates_after_post_delete = LocalSite::read(pool).await?;
|
||||||
|
assert_eq!(1, site_aggregates_after_post_delete.posts);
|
||||||
|
assert_eq!(0, site_aggregates_after_post_delete.comments);
|
||||||
|
|
||||||
|
// This shouuld delete all the associated rows, and fire triggers
|
||||||
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
|
assert_eq!(1, person_num_deleted);
|
||||||
|
|
||||||
|
// Delete the community
|
||||||
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
|
// Site should still exist, it can without a site creator.
|
||||||
|
let after_delete_creator = LocalSite::read(pool).await;
|
||||||
|
assert!(after_delete_creator.is_ok());
|
||||||
|
|
||||||
|
Site::delete(pool, inserted_site.id).await?;
|
||||||
|
let after_delete_site = LocalSite::read(pool).await;
|
||||||
|
assert!(after_delete_site.is_err());
|
||||||
|
|
||||||
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_soft_delete() -> LemmyResult<()> {
|
||||||
|
let pool = &build_db_pool_for_tests();
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
|
let (inserted_instance, inserted_person, inserted_site, inserted_community) =
|
||||||
|
prepare_site_with_community(pool).await?;
|
||||||
|
|
||||||
|
let site_aggregates_before = LocalSite::read(pool).await?;
|
||||||
|
assert_eq!(1, site_aggregates_before.communities);
|
||||||
|
|
||||||
|
Community::update(
|
||||||
|
pool,
|
||||||
|
inserted_community.id,
|
||||||
|
&CommunityUpdateForm {
|
||||||
|
deleted: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let site_aggregates_after_delete = LocalSite::read(pool).await?;
|
||||||
|
assert_eq!(0, site_aggregates_after_delete.communities);
|
||||||
|
|
||||||
|
Community::update(
|
||||||
|
pool,
|
||||||
|
inserted_community.id,
|
||||||
|
&CommunityUpdateForm {
|
||||||
|
deleted: Some(false),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Community::update(
|
||||||
|
pool,
|
||||||
|
inserted_community.id,
|
||||||
|
&CommunityUpdateForm {
|
||||||
|
removed: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let site_aggregates_after_remove = LocalSite::read(pool).await?;
|
||||||
|
assert_eq!(0, site_aggregates_after_remove.communities);
|
||||||
|
|
||||||
|
Community::update(
|
||||||
|
pool,
|
||||||
|
inserted_community.id,
|
||||||
|
&CommunityUpdateForm {
|
||||||
|
deleted: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let site_aggregates_after_remove_delete = LocalSite::read(pool).await?;
|
||||||
|
assert_eq!(0, site_aggregates_after_remove_delete.communities);
|
||||||
|
|
||||||
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
|
Site::delete(pool, inserted_site.id).await?;
|
||||||
|
Person::delete(pool, inserted_person.id).await?;
|
||||||
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ use crate::{
|
||||||
source::{
|
source::{
|
||||||
actor_language::LocalUserLanguage,
|
actor_language::LocalUserLanguage,
|
||||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||||
local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeInsertForm},
|
|
||||||
site::Site,
|
site::Site,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
|
@ -55,10 +54,6 @@ impl LocalUser {
|
||||||
|
|
||||||
LocalUserLanguage::update(pool, languages, local_user_.id).await?;
|
LocalUserLanguage::update(pool, languages, local_user_.id).await?;
|
||||||
|
|
||||||
// Create their vote_display_modes
|
|
||||||
let vote_display_mode_form = LocalUserVoteDisplayModeInsertForm::new(local_user_.id);
|
|
||||||
LocalUserVoteDisplayMode::create(pool, &vote_display_mode_form).await?;
|
|
||||||
|
|
||||||
Ok(local_user_)
|
Ok(local_user_)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
use crate::{
|
|
||||||
diesel::OptionalExtension,
|
|
||||||
newtypes::LocalUserId,
|
|
||||||
schema::local_user_vote_display_mode,
|
|
||||||
source::local_user_vote_display_mode::{
|
|
||||||
LocalUserVoteDisplayMode,
|
|
||||||
LocalUserVoteDisplayModeInsertForm,
|
|
||||||
LocalUserVoteDisplayModeUpdateForm,
|
|
||||||
},
|
|
||||||
utils::{get_conn, DbPool},
|
|
||||||
};
|
|
||||||
use diesel::{dsl::insert_into, result::Error, QueryDsl};
|
|
||||||
use diesel_async::RunQueryDsl;
|
|
||||||
|
|
||||||
impl LocalUserVoteDisplayMode {
|
|
||||||
pub async fn read(pool: &mut DbPool<'_>) -> Result<Option<Self>, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
local_user_vote_display_mode::table
|
|
||||||
.first(conn)
|
|
||||||
.await
|
|
||||||
.optional()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
form: &LocalUserVoteDisplayModeInsertForm,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
insert_into(local_user_vote_display_mode::table)
|
|
||||||
.values(form)
|
|
||||||
.get_result::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
local_user_id: LocalUserId,
|
|
||||||
form: &LocalUserVoteDisplayModeUpdateForm,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
// avoid error "There are no changes to save. This query cannot be built"
|
|
||||||
if form.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::update(local_user_vote_display_mode::table.find(local_user_id))
|
|
||||||
.set(form)
|
|
||||||
.get_result::<Self>(conn)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LocalUserVoteDisplayModeUpdateForm {
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.score.is_none()
|
|
||||||
&& self.upvotes.is_none()
|
|
||||||
&& self.downvotes.is_none()
|
|
||||||
&& self.upvote_percentage.is_none()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,7 +20,6 @@ pub mod local_site;
|
||||||
pub mod local_site_rate_limit;
|
pub mod local_site_rate_limit;
|
||||||
pub mod local_site_url_blocklist;
|
pub mod local_site_url_blocklist;
|
||||||
pub mod local_user;
|
pub mod local_user;
|
||||||
pub mod local_user_vote_display_mode;
|
|
||||||
pub mod login_token;
|
pub mod login_token;
|
||||||
pub mod mod_log;
|
pub mod mod_log;
|
||||||
pub mod oauth_account;
|
pub mod oauth_account;
|
||||||
|
@ -31,6 +30,7 @@ pub mod person_block;
|
||||||
pub mod person_comment_mention;
|
pub mod person_comment_mention;
|
||||||
pub mod person_post_mention;
|
pub mod person_post_mention;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
|
pub mod post_actions;
|
||||||
pub mod post_report;
|
pub mod post_report;
|
||||||
pub mod private_message;
|
pub mod private_message;
|
||||||
pub mod private_message_report;
|
pub mod private_message_report;
|
||||||
|
|
|
@ -256,12 +256,16 @@ mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
source::{
|
source::{
|
||||||
|
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
|
||||||
|
community::{Community, CommunityInsertForm},
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
person::{Person, PersonFollower, PersonFollowerForm, PersonInsertForm, PersonUpdateForm},
|
person::{Person, PersonFollower, PersonFollowerForm, PersonInsertForm, PersonUpdateForm},
|
||||||
|
post::{Post, PostInsertForm, PostLike, PostLikeForm},
|
||||||
},
|
},
|
||||||
traits::{Crud, Followable},
|
traits::{Crud, Followable, Likeable},
|
||||||
utils::{build_db_pool_for_tests, uplete},
|
utils::{build_db_pool_for_tests, uplete},
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
@ -299,6 +303,10 @@ mod tests {
|
||||||
matrix_user_id: None,
|
matrix_user_id: None,
|
||||||
ban_expires: None,
|
ban_expires: None,
|
||||||
instance_id: inserted_instance.id,
|
instance_id: inserted_instance.id,
|
||||||
|
post_count: 0,
|
||||||
|
post_score: 0,
|
||||||
|
comment_count: 0,
|
||||||
|
comment_score: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let read_person = Person::read(pool, inserted_person.id).await?;
|
let read_person = Person::read(pool, inserted_person.id).await?;
|
||||||
|
@ -350,4 +358,151 @@ mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_aggregates() -> Result<(), Error> {
|
||||||
|
let pool = &build_db_pool_for_tests();
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_user_agg");
|
||||||
|
|
||||||
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_user_agg");
|
||||||
|
|
||||||
|
let another_inserted_person = Person::create(pool, &another_person).await?;
|
||||||
|
|
||||||
|
let new_community = CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"TIL_site_agg".into(),
|
||||||
|
"nada".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
|
let new_post = PostInsertForm::new(
|
||||||
|
"A test post".into(),
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_community.id,
|
||||||
|
);
|
||||||
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
|
let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1);
|
||||||
|
let _inserted_post_like = PostLike::like(pool, &post_like).await?;
|
||||||
|
|
||||||
|
let comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
|
let mut comment_like = CommentLikeForm {
|
||||||
|
comment_id: inserted_comment.id,
|
||||||
|
person_id: inserted_person.id,
|
||||||
|
score: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _inserted_comment_like = CommentLike::like(pool, &comment_like).await?;
|
||||||
|
|
||||||
|
let child_comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
let inserted_child_comment =
|
||||||
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
|
|
||||||
|
let child_comment_like = CommentLikeForm {
|
||||||
|
comment_id: inserted_child_comment.id,
|
||||||
|
person_id: another_inserted_person.id,
|
||||||
|
score: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _inserted_child_comment_like = CommentLike::like(pool, &child_comment_like).await?;
|
||||||
|
|
||||||
|
let person_aggregates_before_delete = Person::read(pool, inserted_person.id).await?;
|
||||||
|
|
||||||
|
assert_eq!(1, person_aggregates_before_delete.post_count);
|
||||||
|
assert_eq!(1, person_aggregates_before_delete.post_score);
|
||||||
|
assert_eq!(2, person_aggregates_before_delete.comment_count);
|
||||||
|
assert_eq!(2, person_aggregates_before_delete.comment_score);
|
||||||
|
|
||||||
|
// Remove a post like
|
||||||
|
PostLike::remove(pool, inserted_person.id, inserted_post.id).await?;
|
||||||
|
let after_post_like_remove = Person::read(pool, inserted_person.id).await?;
|
||||||
|
assert_eq!(0, after_post_like_remove.post_score);
|
||||||
|
|
||||||
|
Comment::update(
|
||||||
|
pool,
|
||||||
|
inserted_comment.id,
|
||||||
|
&CommentUpdateForm {
|
||||||
|
removed: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Comment::update(
|
||||||
|
pool,
|
||||||
|
inserted_child_comment.id,
|
||||||
|
&CommentUpdateForm {
|
||||||
|
removed: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let after_parent_comment_removed = Person::read(pool, inserted_person.id).await?;
|
||||||
|
assert_eq!(0, after_parent_comment_removed.comment_count);
|
||||||
|
// TODO: fix person aggregate comment score calculation
|
||||||
|
// assert_eq!(0, after_parent_comment_removed.comment_score);
|
||||||
|
|
||||||
|
// Remove a parent comment (the scores should also be removed)
|
||||||
|
Comment::delete(pool, inserted_comment.id).await?;
|
||||||
|
Comment::delete(pool, inserted_child_comment.id).await?;
|
||||||
|
let after_parent_comment_delete = Person::read(pool, inserted_person.id).await?;
|
||||||
|
assert_eq!(0, after_parent_comment_delete.comment_count);
|
||||||
|
// TODO: fix person aggregate comment score calculation
|
||||||
|
// assert_eq!(0, after_parent_comment_delete.comment_score);
|
||||||
|
|
||||||
|
// Add in the two comments again, then delete the post.
|
||||||
|
let new_parent_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
let _new_child_comment =
|
||||||
|
Comment::create(pool, &child_comment_form, Some(&new_parent_comment.path)).await?;
|
||||||
|
comment_like.comment_id = new_parent_comment.id;
|
||||||
|
CommentLike::like(pool, &comment_like).await?;
|
||||||
|
let after_comment_add = Person::read(pool, inserted_person.id).await?;
|
||||||
|
assert_eq!(2, after_comment_add.comment_count);
|
||||||
|
// TODO: fix person aggregate comment score calculation
|
||||||
|
// assert_eq!(1, after_comment_add.comment_score);
|
||||||
|
|
||||||
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
|
let after_post_delete = Person::read(pool, inserted_person.id).await?;
|
||||||
|
// TODO: fix person aggregate comment score calculation
|
||||||
|
// assert_eq!(0, after_post_delete.comment_score);
|
||||||
|
assert_eq!(0, after_post_delete.comment_count);
|
||||||
|
assert_eq!(0, after_post_delete.post_score);
|
||||||
|
assert_eq!(0, after_post_delete.post_count);
|
||||||
|
|
||||||
|
// This should delete all the associated rows, and fire triggers
|
||||||
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
|
assert_eq!(1, person_num_deleted);
|
||||||
|
Person::delete(pool, another_inserted_person.id).await?;
|
||||||
|
|
||||||
|
// Delete the community
|
||||||
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
|
// Should be none found
|
||||||
|
let after_delete = Person::read(pool, inserted_person.id).await;
|
||||||
|
assert!(after_delete.is_err());
|
||||||
|
|
||||||
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
traits::{Crud, Likeable, Saveable},
|
traits::{Crud, Likeable, Saveable},
|
||||||
utils::{
|
utils::{
|
||||||
functions::coalesce,
|
functions::{coalesce, hot_rank, scaled_rank},
|
||||||
get_conn,
|
get_conn,
|
||||||
now,
|
now,
|
||||||
uplete,
|
uplete,
|
||||||
|
@ -38,6 +38,7 @@ use diesel::{
|
||||||
BoolExpressionMethods,
|
BoolExpressionMethods,
|
||||||
DecoratableTarget,
|
DecoratableTarget,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
|
JoinOnDsl,
|
||||||
NullableExpressionMethods,
|
NullableExpressionMethods,
|
||||||
OptionalExtension,
|
OptionalExtension,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
|
@ -289,6 +290,33 @@ impl Post {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn update_ranks(pool: &mut DbPool<'_>, post_id: PostId) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
|
// Diesel can't update based on a join, which is necessary for the scaled_rank
|
||||||
|
// https://github.com/diesel-rs/diesel/issues/1478
|
||||||
|
// Just select the metrics we need manually, for now, since its a single post anyway
|
||||||
|
|
||||||
|
let interactions_month = community::table
|
||||||
|
.select(community::interactions_month)
|
||||||
|
.inner_join(post::table.on(community::id.eq(post::community_id)))
|
||||||
|
.filter(post::id.eq(post_id))
|
||||||
|
.first::<i64>(conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
diesel::update(post::table.find(post_id))
|
||||||
|
.set((
|
||||||
|
post::hot_rank.eq(hot_rank(post::score, post::published)),
|
||||||
|
post::hot_rank_active.eq(hot_rank(post::score, post::newest_comment_time_necro)),
|
||||||
|
post::scaled_rank.eq(scaled_rank(
|
||||||
|
post::score,
|
||||||
|
post::published,
|
||||||
|
interactions_month,
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
pub fn local_url(&self, settings: &Settings) -> LemmyResult<DbUrl> {
|
pub fn local_url(&self, settings: &Settings) -> LemmyResult<DbUrl> {
|
||||||
let domain = settings.get_protocol_and_hostname();
|
let domain = settings.get_protocol_and_hostname();
|
||||||
Ok(Url::parse(&format!("{domain}/post/{}", self.id))?.into())
|
Ok(Url::parse(&format!("{domain}/post/{}", self.id))?.into())
|
||||||
|
@ -463,9 +491,9 @@ impl PostActionsCursor {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
source::{
|
source::{
|
||||||
|
comment::{Comment, CommentInsertForm, CommentUpdateForm},
|
||||||
community::{Community, CommunityInsertForm},
|
community::{Community, CommunityInsertForm},
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
|
@ -482,9 +510,10 @@ mod tests {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
traits::{Crud, Likeable, Saveable},
|
traits::{Crud, Likeable, Saveable},
|
||||||
utils::{build_db_pool_for_tests, uplete},
|
utils::{build_db_pool_for_tests, uplete, RANK_DEFAULT},
|
||||||
};
|
};
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
|
use diesel::result::Error;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
@ -556,6 +585,19 @@ mod tests {
|
||||||
featured_local: false,
|
featured_local: false,
|
||||||
url_content_type: None,
|
url_content_type: None,
|
||||||
scheduled_publish_time: None,
|
scheduled_publish_time: None,
|
||||||
|
comments: 0,
|
||||||
|
controversy_rank: 0.0,
|
||||||
|
downvotes: 0,
|
||||||
|
upvotes: 1,
|
||||||
|
score: 1,
|
||||||
|
hot_rank: RANK_DEFAULT,
|
||||||
|
hot_rank_active: RANK_DEFAULT,
|
||||||
|
instance_id: inserted_instance.id,
|
||||||
|
newest_comment_time: inserted_post.published,
|
||||||
|
newest_comment_time_necro: inserted_post.published,
|
||||||
|
report_count: 0,
|
||||||
|
scaled_rank: RANK_DEFAULT,
|
||||||
|
unresolved_report_count: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Post Like
|
// Post Like
|
||||||
|
@ -622,11 +664,210 @@ mod tests {
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
assert_eq!(expected_post, read_post);
|
assert_eq!(expected_post, read_post);
|
||||||
assert_eq!(expected_post, inserted_post);
|
|
||||||
assert_eq!(expected_post, updated_post);
|
assert_eq!(expected_post, updated_post);
|
||||||
assert_eq!(expected_post_like, inserted_post_like);
|
assert_eq!(expected_post_like, inserted_post_like);
|
||||||
assert_eq!(expected_post_saved, inserted_post_saved);
|
assert_eq!(expected_post_saved, inserted_post_saved);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_aggregates() -> Result<(), Error> {
|
||||||
|
let pool = &build_db_pool_for_tests();
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
||||||
|
|
||||||
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
||||||
|
|
||||||
|
let another_inserted_person = Person::create(pool, &another_person).await?;
|
||||||
|
|
||||||
|
let new_community = CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"TIL_community_agg".into(),
|
||||||
|
"nada".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
);
|
||||||
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
|
let new_post = PostInsertForm::new(
|
||||||
|
"A test post".into(),
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_community.id,
|
||||||
|
);
|
||||||
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
|
let comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
|
let child_comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
let inserted_child_comment =
|
||||||
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
|
|
||||||
|
let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1);
|
||||||
|
|
||||||
|
PostLike::like(pool, &post_like).await?;
|
||||||
|
|
||||||
|
let post_aggs_before_delete = Post::read(pool, inserted_post.id).await?;
|
||||||
|
|
||||||
|
assert_eq!(2, post_aggs_before_delete.comments);
|
||||||
|
assert_eq!(1, post_aggs_before_delete.score);
|
||||||
|
assert_eq!(1, post_aggs_before_delete.upvotes);
|
||||||
|
assert_eq!(0, post_aggs_before_delete.downvotes);
|
||||||
|
|
||||||
|
// Add a post dislike from the other person
|
||||||
|
let post_dislike = PostLikeForm::new(inserted_post.id, another_inserted_person.id, -1);
|
||||||
|
|
||||||
|
PostLike::like(pool, &post_dislike).await?;
|
||||||
|
|
||||||
|
let post_aggs_after_dislike = Post::read(pool, inserted_post.id).await?;
|
||||||
|
|
||||||
|
assert_eq!(2, post_aggs_after_dislike.comments);
|
||||||
|
assert_eq!(0, post_aggs_after_dislike.score);
|
||||||
|
assert_eq!(1, post_aggs_after_dislike.upvotes);
|
||||||
|
assert_eq!(1, post_aggs_after_dislike.downvotes);
|
||||||
|
|
||||||
|
// Remove the comments
|
||||||
|
Comment::delete(pool, inserted_comment.id).await?;
|
||||||
|
Comment::delete(pool, inserted_child_comment.id).await?;
|
||||||
|
let after_comment_delete = Post::read(pool, inserted_post.id).await?;
|
||||||
|
assert_eq!(0, after_comment_delete.comments);
|
||||||
|
assert_eq!(0, after_comment_delete.score);
|
||||||
|
assert_eq!(1, after_comment_delete.upvotes);
|
||||||
|
assert_eq!(1, after_comment_delete.downvotes);
|
||||||
|
|
||||||
|
// Remove the first post like
|
||||||
|
PostLike::remove(pool, inserted_person.id, inserted_post.id).await?;
|
||||||
|
let after_like_remove = Post::read(pool, inserted_post.id).await?;
|
||||||
|
assert_eq!(0, after_like_remove.comments);
|
||||||
|
assert_eq!(-1, after_like_remove.score);
|
||||||
|
assert_eq!(0, after_like_remove.upvotes);
|
||||||
|
assert_eq!(1, after_like_remove.downvotes);
|
||||||
|
|
||||||
|
// This should delete all the associated rows, and fire triggers
|
||||||
|
Person::delete(pool, another_inserted_person.id).await?;
|
||||||
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
|
assert_eq!(1, person_num_deleted);
|
||||||
|
|
||||||
|
// Delete the community
|
||||||
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
|
// Should be none found, since the creator was deleted
|
||||||
|
let after_delete = Post::read(pool, inserted_post.id).await;
|
||||||
|
assert!(after_delete.is_err());
|
||||||
|
|
||||||
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_aggregates_soft_delete() -> Result<(), Error> {
|
||||||
|
let pool = &build_db_pool_for_tests();
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
||||||
|
|
||||||
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
|
let new_community = CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"TIL_community_agg".into(),
|
||||||
|
"nada".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
);
|
||||||
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
|
let new_post = PostInsertForm::new(
|
||||||
|
"A test post".into(),
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_community.id,
|
||||||
|
);
|
||||||
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
|
let comment_form = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post.id,
|
||||||
|
"A test comment".into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
|
let post_aggregates_before = Post::read(pool, inserted_post.id).await?;
|
||||||
|
assert_eq!(1, post_aggregates_before.comments);
|
||||||
|
|
||||||
|
Comment::update(
|
||||||
|
pool,
|
||||||
|
inserted_comment.id,
|
||||||
|
&CommentUpdateForm {
|
||||||
|
removed: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let post_aggregates_after_remove = Post::read(pool, inserted_post.id).await?;
|
||||||
|
assert_eq!(0, post_aggregates_after_remove.comments);
|
||||||
|
|
||||||
|
Comment::update(
|
||||||
|
pool,
|
||||||
|
inserted_comment.id,
|
||||||
|
&CommentUpdateForm {
|
||||||
|
removed: Some(false),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Comment::update(
|
||||||
|
pool,
|
||||||
|
inserted_comment.id,
|
||||||
|
&CommentUpdateForm {
|
||||||
|
deleted: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let post_aggregates_after_delete = Post::read(pool, inserted_post.id).await?;
|
||||||
|
assert_eq!(0, post_aggregates_after_delete.comments);
|
||||||
|
|
||||||
|
Comment::update(
|
||||||
|
pool,
|
||||||
|
inserted_comment.id,
|
||||||
|
&CommentUpdateForm {
|
||||||
|
removed: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let post_aggregates_after_delete_remove = Post::read(pool, inserted_post.id).await?;
|
||||||
|
assert_eq!(0, post_aggregates_after_delete_remove.comments);
|
||||||
|
|
||||||
|
Comment::delete(pool, inserted_comment.id).await?;
|
||||||
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
|
Person::delete(pool, inserted_person.id).await?;
|
||||||
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
|
|
||||||
diesel::OptionalExtension,
|
diesel::OptionalExtension,
|
||||||
newtypes::{PersonId, PostId},
|
newtypes::{PersonId, PostId},
|
||||||
schema::post_actions,
|
schema::post_actions,
|
||||||
|
source::post_actions::{PostActions, PostActionsForm},
|
||||||
utils::{get_conn, now, DbPool},
|
utils::{get_conn, now, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
|
@ -15,11 +15,8 @@ use diesel::{
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
impl PersonPostAggregates {
|
impl PostActions {
|
||||||
pub async fn upsert(
|
pub async fn upsert(pool: &mut DbPool<'_>, form: &PostActionsForm) -> Result<Self, Error> {
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
form: &PersonPostAggregatesForm,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
let form = (form, post_actions::read_comments.eq(now().nullable()));
|
let form = (form, post_actions::read_comments.eq(now().nullable()));
|
||||||
insert_into(post_actions::table)
|
insert_into(post_actions::table)
|
|
@ -9,7 +9,6 @@ extern crate diesel_derive_newtype;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate diesel_derive_enum;
|
extern crate diesel_derive_enum;
|
||||||
|
|
||||||
pub mod aggregates;
|
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod impls;
|
pub mod impls;
|
||||||
pub mod newtypes;
|
pub mod newtypes;
|
||||||
|
@ -337,4 +336,8 @@ pub type Person1AliasAllColumnsTuple = (
|
||||||
AliasedField<aliases::Person1, person::bot_account>,
|
AliasedField<aliases::Person1, person::bot_account>,
|
||||||
AliasedField<aliases::Person1, person::ban_expires>,
|
AliasedField<aliases::Person1, person::ban_expires>,
|
||||||
AliasedField<aliases::Person1, person::instance_id>,
|
AliasedField<aliases::Person1, person::instance_id>,
|
||||||
|
AliasedField<aliases::Person1, person::post_count>,
|
||||||
|
AliasedField<aliases::Person1, person::post_score>,
|
||||||
|
AliasedField<aliases::Person1, person::comment_count>,
|
||||||
|
AliasedField<aliases::Person1, person::comment_score>,
|
||||||
);
|
);
|
||||||
|
|
|
@ -130,6 +130,14 @@ diesel::table! {
|
||||||
path -> Ltree,
|
path -> Ltree,
|
||||||
distinguished -> Bool,
|
distinguished -> Bool,
|
||||||
language_id -> Int4,
|
language_id -> Int4,
|
||||||
|
score -> Int8,
|
||||||
|
upvotes -> Int8,
|
||||||
|
downvotes -> Int8,
|
||||||
|
child_count -> Int4,
|
||||||
|
hot_rank -> Float8,
|
||||||
|
controversy_rank -> Float8,
|
||||||
|
report_count -> Int2,
|
||||||
|
unresolved_report_count -> Int2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,21 +151,6 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
comment_aggregates (comment_id) {
|
|
||||||
comment_id -> Int4,
|
|
||||||
score -> Int8,
|
|
||||||
upvotes -> Int8,
|
|
||||||
downvotes -> Int8,
|
|
||||||
published -> Timestamptz,
|
|
||||||
child_count -> Int4,
|
|
||||||
hot_rank -> Float8,
|
|
||||||
controversy_rank -> Float8,
|
|
||||||
report_count -> Int2,
|
|
||||||
unresolved_report_count -> Int2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
comment_reply (id) {
|
comment_reply (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -222,6 +215,18 @@ diesel::table! {
|
||||||
#[max_length = 150]
|
#[max_length = 150]
|
||||||
description -> Nullable<Varchar>,
|
description -> Nullable<Varchar>,
|
||||||
random_number -> Int2,
|
random_number -> Int2,
|
||||||
|
subscribers -> Int8,
|
||||||
|
posts -> Int8,
|
||||||
|
comments -> Int8,
|
||||||
|
users_active_day -> Int8,
|
||||||
|
users_active_week -> Int8,
|
||||||
|
users_active_month -> Int8,
|
||||||
|
users_active_half_year -> Int8,
|
||||||
|
hot_rank -> Float8,
|
||||||
|
subscribers_local -> Int8,
|
||||||
|
report_count -> Int2,
|
||||||
|
unresolved_report_count -> Int2,
|
||||||
|
interactions_month -> Int8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,25 +247,6 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
community_aggregates (community_id) {
|
|
||||||
community_id -> Int4,
|
|
||||||
subscribers -> Int8,
|
|
||||||
posts -> Int8,
|
|
||||||
comments -> Int8,
|
|
||||||
published -> Timestamptz,
|
|
||||||
users_active_day -> Int8,
|
|
||||||
users_active_week -> Int8,
|
|
||||||
users_active_month -> Int8,
|
|
||||||
users_active_half_year -> Int8,
|
|
||||||
hot_rank -> Float8,
|
|
||||||
subscribers_local -> Int8,
|
|
||||||
report_count -> Int2,
|
|
||||||
unresolved_report_count -> Int2,
|
|
||||||
interactions_month -> Int8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
community_language (community_id, language_id) {
|
community_language (community_id, language_id) {
|
||||||
community_id -> Int4,
|
community_id -> Int4,
|
||||||
|
@ -449,6 +435,14 @@ diesel::table! {
|
||||||
comment_downvotes -> FederationModeEnum,
|
comment_downvotes -> FederationModeEnum,
|
||||||
disable_donation_dialog -> Bool,
|
disable_donation_dialog -> Bool,
|
||||||
default_post_time_range_seconds -> Nullable<Int4>,
|
default_post_time_range_seconds -> Nullable<Int4>,
|
||||||
|
users -> Int8,
|
||||||
|
posts -> Int8,
|
||||||
|
comments -> Int8,
|
||||||
|
communities -> Int8,
|
||||||
|
users_active_day -> Int8,
|
||||||
|
users_active_week -> Int8,
|
||||||
|
users_active_month -> Int8,
|
||||||
|
users_active_half_year -> Int8,
|
||||||
disallow_nsfw_content -> Bool,
|
disallow_nsfw_content -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -524,6 +518,10 @@ diesel::table! {
|
||||||
last_donation_notification -> Timestamptz,
|
last_donation_notification -> Timestamptz,
|
||||||
hide_media -> Bool,
|
hide_media -> Bool,
|
||||||
default_post_time_range_seconds -> Nullable<Int4>,
|
default_post_time_range_seconds -> Nullable<Int4>,
|
||||||
|
show_score -> Bool,
|
||||||
|
show_upvotes -> Bool,
|
||||||
|
show_downvotes -> Bool,
|
||||||
|
show_upvote_percentage -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,16 +532,6 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
local_user_vote_display_mode (local_user_id) {
|
|
||||||
local_user_id -> Int4,
|
|
||||||
score -> Bool,
|
|
||||||
upvotes -> Bool,
|
|
||||||
downvotes -> Bool,
|
|
||||||
upvote_percentage -> Bool,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
login_token (token) {
|
login_token (token) {
|
||||||
token -> Text,
|
token -> Text,
|
||||||
|
@ -765,6 +753,10 @@ diesel::table! {
|
||||||
bot_account -> Bool,
|
bot_account -> Bool,
|
||||||
ban_expires -> Nullable<Timestamptz>,
|
ban_expires -> Nullable<Timestamptz>,
|
||||||
instance_id -> Int4,
|
instance_id -> Int4,
|
||||||
|
post_count -> Int8,
|
||||||
|
post_score -> Int8,
|
||||||
|
comment_count -> Int8,
|
||||||
|
comment_score -> Int8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -778,17 +770,6 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
person_aggregates (person_id) {
|
|
||||||
person_id -> Int4,
|
|
||||||
post_count -> Int8,
|
|
||||||
post_score -> Int8,
|
|
||||||
comment_count -> Int8,
|
|
||||||
comment_score -> Int8,
|
|
||||||
published -> Timestamptz,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
person_ban (person_id) {
|
person_ban (person_id) {
|
||||||
person_id -> Int4,
|
person_id -> Int4,
|
||||||
|
@ -864,6 +845,19 @@ diesel::table! {
|
||||||
url_content_type -> Nullable<Text>,
|
url_content_type -> Nullable<Text>,
|
||||||
alt_text -> Nullable<Text>,
|
alt_text -> Nullable<Text>,
|
||||||
scheduled_publish_time -> Nullable<Timestamptz>,
|
scheduled_publish_time -> Nullable<Timestamptz>,
|
||||||
|
comments -> Int8,
|
||||||
|
score -> Int8,
|
||||||
|
upvotes -> Int8,
|
||||||
|
downvotes -> Int8,
|
||||||
|
newest_comment_time_necro -> Timestamptz,
|
||||||
|
newest_comment_time -> Timestamptz,
|
||||||
|
hot_rank -> Float8,
|
||||||
|
hot_rank_active -> Float8,
|
||||||
|
controversy_rank -> Float8,
|
||||||
|
instance_id -> Int4,
|
||||||
|
scaled_rank -> Float8,
|
||||||
|
report_count -> Int2,
|
||||||
|
unresolved_report_count -> Int2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -881,30 +875,6 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
post_aggregates (post_id) {
|
|
||||||
post_id -> Int4,
|
|
||||||
comments -> Int8,
|
|
||||||
score -> Int8,
|
|
||||||
upvotes -> Int8,
|
|
||||||
downvotes -> Int8,
|
|
||||||
published -> Timestamptz,
|
|
||||||
newest_comment_time_necro -> Timestamptz,
|
|
||||||
newest_comment_time -> Timestamptz,
|
|
||||||
featured_community -> Bool,
|
|
||||||
featured_local -> Bool,
|
|
||||||
hot_rank -> Float8,
|
|
||||||
hot_rank_active -> Float8,
|
|
||||||
community_id -> Int4,
|
|
||||||
creator_id -> Int4,
|
|
||||||
controversy_rank -> Float8,
|
|
||||||
instance_id -> Int4,
|
|
||||||
scaled_rank -> Float8,
|
|
||||||
report_count -> Int2,
|
|
||||||
unresolved_report_count -> Int2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
post_report (id) {
|
post_report (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -1066,20 +1036,6 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
site_aggregates (site_id) {
|
|
||||||
site_id -> Int4,
|
|
||||||
users -> Int8,
|
|
||||||
posts -> Int8,
|
|
||||||
comments -> Int8,
|
|
||||||
communities -> Int8,
|
|
||||||
users_active_day -> Int8,
|
|
||||||
users_active_week -> Int8,
|
|
||||||
users_active_month -> Int8,
|
|
||||||
users_active_half_year -> Int8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
site_language (site_id, language_id) {
|
site_language (site_id, language_id) {
|
||||||
site_id -> Int4,
|
site_id -> Int4,
|
||||||
|
@ -1123,13 +1079,11 @@ diesel::joinable!(comment -> person (creator_id));
|
||||||
diesel::joinable!(comment -> post (post_id));
|
diesel::joinable!(comment -> post (post_id));
|
||||||
diesel::joinable!(comment_actions -> comment (comment_id));
|
diesel::joinable!(comment_actions -> comment (comment_id));
|
||||||
diesel::joinable!(comment_actions -> person (person_id));
|
diesel::joinable!(comment_actions -> person (person_id));
|
||||||
diesel::joinable!(comment_aggregates -> comment (comment_id));
|
|
||||||
diesel::joinable!(comment_reply -> comment (comment_id));
|
diesel::joinable!(comment_reply -> comment (comment_id));
|
||||||
diesel::joinable!(comment_reply -> person (recipient_id));
|
diesel::joinable!(comment_reply -> person (recipient_id));
|
||||||
diesel::joinable!(comment_report -> comment (comment_id));
|
diesel::joinable!(comment_report -> comment (comment_id));
|
||||||
diesel::joinable!(community -> instance (instance_id));
|
diesel::joinable!(community -> instance (instance_id));
|
||||||
diesel::joinable!(community_actions -> community (community_id));
|
diesel::joinable!(community_actions -> community (community_id));
|
||||||
diesel::joinable!(community_aggregates -> community (community_id));
|
|
||||||
diesel::joinable!(community_language -> community (community_id));
|
diesel::joinable!(community_language -> community (community_id));
|
||||||
diesel::joinable!(community_language -> language (language_id));
|
diesel::joinable!(community_language -> language (language_id));
|
||||||
diesel::joinable!(community_report -> community (community_id));
|
diesel::joinable!(community_report -> community (community_id));
|
||||||
|
@ -1150,7 +1104,6 @@ diesel::joinable!(local_site_rate_limit -> local_site (local_site_id));
|
||||||
diesel::joinable!(local_user -> person (person_id));
|
diesel::joinable!(local_user -> person (person_id));
|
||||||
diesel::joinable!(local_user_language -> language (language_id));
|
diesel::joinable!(local_user_language -> language (language_id));
|
||||||
diesel::joinable!(local_user_language -> local_user (local_user_id));
|
diesel::joinable!(local_user_language -> local_user (local_user_id));
|
||||||
diesel::joinable!(local_user_vote_display_mode -> local_user (local_user_id));
|
|
||||||
diesel::joinable!(login_token -> local_user (user_id));
|
diesel::joinable!(login_token -> local_user (user_id));
|
||||||
diesel::joinable!(mod_add_community -> community (community_id));
|
diesel::joinable!(mod_add_community -> community (community_id));
|
||||||
diesel::joinable!(mod_ban_from_community -> community (community_id));
|
diesel::joinable!(mod_ban_from_community -> community (community_id));
|
||||||
|
@ -1188,7 +1141,6 @@ diesel::joinable!(oauth_account -> local_user (local_user_id));
|
||||||
diesel::joinable!(oauth_account -> oauth_provider (oauth_provider_id));
|
diesel::joinable!(oauth_account -> oauth_provider (oauth_provider_id));
|
||||||
diesel::joinable!(password_reset_request -> local_user (local_user_id));
|
diesel::joinable!(password_reset_request -> local_user (local_user_id));
|
||||||
diesel::joinable!(person -> instance (instance_id));
|
diesel::joinable!(person -> instance (instance_id));
|
||||||
diesel::joinable!(person_aggregates -> person (person_id));
|
|
||||||
diesel::joinable!(person_ban -> person (person_id));
|
diesel::joinable!(person_ban -> person (person_id));
|
||||||
diesel::joinable!(person_comment_mention -> comment (comment_id));
|
diesel::joinable!(person_comment_mention -> comment (comment_id));
|
||||||
diesel::joinable!(person_comment_mention -> person (recipient_id));
|
diesel::joinable!(person_comment_mention -> person (recipient_id));
|
||||||
|
@ -1200,14 +1152,11 @@ diesel::joinable!(person_saved_combined -> comment (comment_id));
|
||||||
diesel::joinable!(person_saved_combined -> person (person_id));
|
diesel::joinable!(person_saved_combined -> person (person_id));
|
||||||
diesel::joinable!(person_saved_combined -> post (post_id));
|
diesel::joinable!(person_saved_combined -> post (post_id));
|
||||||
diesel::joinable!(post -> community (community_id));
|
diesel::joinable!(post -> community (community_id));
|
||||||
|
diesel::joinable!(post -> instance (instance_id));
|
||||||
diesel::joinable!(post -> language (language_id));
|
diesel::joinable!(post -> language (language_id));
|
||||||
diesel::joinable!(post -> person (creator_id));
|
diesel::joinable!(post -> person (creator_id));
|
||||||
diesel::joinable!(post_actions -> person (person_id));
|
diesel::joinable!(post_actions -> person (person_id));
|
||||||
diesel::joinable!(post_actions -> post (post_id));
|
diesel::joinable!(post_actions -> post (post_id));
|
||||||
diesel::joinable!(post_aggregates -> community (community_id));
|
|
||||||
diesel::joinable!(post_aggregates -> instance (instance_id));
|
|
||||||
diesel::joinable!(post_aggregates -> person (creator_id));
|
|
||||||
diesel::joinable!(post_aggregates -> post (post_id));
|
|
||||||
diesel::joinable!(post_report -> post (post_id));
|
diesel::joinable!(post_report -> post (post_id));
|
||||||
diesel::joinable!(post_tag -> post (post_id));
|
diesel::joinable!(post_tag -> post (post_id));
|
||||||
diesel::joinable!(post_tag -> tag (tag_id));
|
diesel::joinable!(post_tag -> tag (tag_id));
|
||||||
|
@ -1223,7 +1172,6 @@ diesel::joinable!(search_combined -> community (community_id));
|
||||||
diesel::joinable!(search_combined -> person (person_id));
|
diesel::joinable!(search_combined -> person (person_id));
|
||||||
diesel::joinable!(search_combined -> post (post_id));
|
diesel::joinable!(search_combined -> post (post_id));
|
||||||
diesel::joinable!(site -> instance (instance_id));
|
diesel::joinable!(site -> instance (instance_id));
|
||||||
diesel::joinable!(site_aggregates -> site (site_id));
|
|
||||||
diesel::joinable!(site_language -> language (language_id));
|
diesel::joinable!(site_language -> language (language_id));
|
||||||
diesel::joinable!(site_language -> site (site_id));
|
diesel::joinable!(site_language -> site (site_id));
|
||||||
diesel::joinable!(tag -> community (community_id));
|
diesel::joinable!(tag -> community (community_id));
|
||||||
|
@ -1238,12 +1186,10 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
captcha_answer,
|
captcha_answer,
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
comment_aggregates,
|
|
||||||
comment_reply,
|
comment_reply,
|
||||||
comment_report,
|
comment_report,
|
||||||
community,
|
community,
|
||||||
community_actions,
|
community_actions,
|
||||||
community_aggregates,
|
|
||||||
community_language,
|
community_language,
|
||||||
community_report,
|
community_report,
|
||||||
custom_emoji,
|
custom_emoji,
|
||||||
|
@ -1263,7 +1209,6 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
local_site_url_blocklist,
|
local_site_url_blocklist,
|
||||||
local_user,
|
local_user,
|
||||||
local_user_language,
|
local_user_language,
|
||||||
local_user_vote_display_mode,
|
|
||||||
login_token,
|
login_token,
|
||||||
mod_add,
|
mod_add,
|
||||||
mod_add_community,
|
mod_add_community,
|
||||||
|
@ -1282,7 +1227,6 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
password_reset_request,
|
password_reset_request,
|
||||||
person,
|
person,
|
||||||
person_actions,
|
person_actions,
|
||||||
person_aggregates,
|
|
||||||
person_ban,
|
person_ban,
|
||||||
person_comment_mention,
|
person_comment_mention,
|
||||||
person_content_combined,
|
person_content_combined,
|
||||||
|
@ -1290,7 +1234,6 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
person_saved_combined,
|
person_saved_combined,
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
|
||||||
post_report,
|
post_report,
|
||||||
post_tag,
|
post_tag,
|
||||||
previously_run_sql,
|
previously_run_sql,
|
||||||
|
@ -1304,7 +1247,6 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
secret,
|
secret,
|
||||||
sent_activity,
|
sent_activity,
|
||||||
site,
|
site,
|
||||||
site_aggregates,
|
|
||||||
site_language,
|
site_language,
|
||||||
tag,
|
tag,
|
||||||
tagline,
|
tagline,
|
||||||
|
|
|
@ -14,7 +14,7 @@ use serde_with::skip_serializing_none;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "full",
|
feature = "full",
|
||||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||||
|
@ -51,6 +51,17 @@ pub struct Comment {
|
||||||
/// Whether the comment has been distinguished(speaking officially) by a mod.
|
/// Whether the comment has been distinguished(speaking officially) by a mod.
|
||||||
pub distinguished: bool,
|
pub distinguished: bool,
|
||||||
pub language_id: LanguageId,
|
pub language_id: LanguageId,
|
||||||
|
pub score: i64,
|
||||||
|
pub upvotes: i64,
|
||||||
|
pub downvotes: i64,
|
||||||
|
/// The total number of children in this comment branch.
|
||||||
|
pub child_count: i32,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub hot_rank: f64,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub controversy_rank: f64,
|
||||||
|
pub report_count: i16,
|
||||||
|
pub unresolved_report_count: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, derive_new::new)]
|
#[derive(Debug, Clone, derive_new::new)]
|
||||||
|
|
|
@ -16,7 +16,7 @@ use strum::{Display, EnumString};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = community))]
|
#[cfg_attr(feature = "full", diesel(table_name = community))]
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
@ -78,6 +78,25 @@ pub struct Community {
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub random_number: i16,
|
pub random_number: i16,
|
||||||
|
pub subscribers: i64,
|
||||||
|
pub posts: i64,
|
||||||
|
pub comments: i64,
|
||||||
|
/// The number of users with any activity in the last day.
|
||||||
|
pub users_active_day: i64,
|
||||||
|
/// The number of users with any activity in the last week.
|
||||||
|
pub users_active_week: i64,
|
||||||
|
/// The number of users with any activity in the last month.
|
||||||
|
pub users_active_month: i64,
|
||||||
|
/// The number of users with any activity in the last year.
|
||||||
|
pub users_active_half_year: i64,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub hot_rank: f64,
|
||||||
|
pub subscribers_local: i64,
|
||||||
|
pub report_count: i16,
|
||||||
|
pub unresolved_report_count: i16,
|
||||||
|
/// Number of any interactions over the last month.
|
||||||
|
#[serde(skip)]
|
||||||
|
pub interactions_month: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, derive_new::new)]
|
#[derive(Debug, Clone, derive_new::new)]
|
||||||
|
|
|
@ -89,6 +89,18 @@ pub struct LocalSite {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
/// A default time range limit to apply to post sorts, in seconds.
|
/// A default time range limit to apply to post sorts, in seconds.
|
||||||
pub default_post_time_range_seconds: Option<i32>,
|
pub default_post_time_range_seconds: Option<i32>,
|
||||||
|
pub users: i64,
|
||||||
|
pub posts: i64,
|
||||||
|
pub comments: i64,
|
||||||
|
pub communities: i64,
|
||||||
|
/// The number of users with any activity in the last day.
|
||||||
|
pub users_active_day: i64,
|
||||||
|
/// The number of users with any activity in the last week.
|
||||||
|
pub users_active_week: i64,
|
||||||
|
/// The number of users with any activity in the last month.
|
||||||
|
pub users_active_month: i64,
|
||||||
|
/// The number of users with any activity in the last half year.
|
||||||
|
pub users_active_half_year: i64,
|
||||||
/// Block NSFW content being created
|
/// Block NSFW content being created
|
||||||
pub disallow_nsfw_content: bool,
|
pub disallow_nsfw_content: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,10 @@ pub struct LocalUser {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
/// A default time range limit to apply to post sorts, in seconds.
|
/// A default time range limit to apply to post sorts, in seconds.
|
||||||
pub default_post_time_range_seconds: Option<i32>,
|
pub default_post_time_range_seconds: Option<i32>,
|
||||||
|
pub show_score: bool,
|
||||||
|
pub show_upvotes: bool,
|
||||||
|
pub show_downvotes: bool,
|
||||||
|
pub show_upvote_percentage: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, derive_new::new)]
|
#[derive(Clone, derive_new::new)]
|
||||||
|
@ -143,6 +147,14 @@ pub struct LocalUserInsertForm {
|
||||||
pub hide_media: Option<bool>,
|
pub hide_media: Option<bool>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub default_post_time_range_seconds: Option<Option<i32>>,
|
pub default_post_time_range_seconds: Option<Option<i32>>,
|
||||||
|
#[new(default)]
|
||||||
|
pub show_score: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
|
pub show_upvotes: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
|
pub show_downvotes: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
|
pub show_upvote_percentage: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -178,4 +190,8 @@ pub struct LocalUserUpdateForm {
|
||||||
pub last_donation_notification: Option<DateTime<Utc>>,
|
pub last_donation_notification: Option<DateTime<Utc>>,
|
||||||
pub hide_media: Option<bool>,
|
pub hide_media: Option<bool>,
|
||||||
pub default_post_time_range_seconds: Option<Option<i32>>,
|
pub default_post_time_range_seconds: Option<Option<i32>>,
|
||||||
|
pub show_score: Option<bool>,
|
||||||
|
pub show_upvotes: Option<bool>,
|
||||||
|
pub show_downvotes: Option<bool>,
|
||||||
|
pub show_upvote_percentage: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
use crate::newtypes::LocalUserId;
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use crate::schema::local_user_vote_display_mode;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_with::skip_serializing_none;
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use ts_rs::TS;
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone, Default, Serialize, Deserialize)]
|
|
||||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = local_user_vote_display_mode))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(primary_key(local_user_id)))]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "full",
|
|
||||||
diesel(belongs_to(crate::source::local_site::LocalUser))
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// The vote display settings for your user.
|
|
||||||
pub struct LocalUserVoteDisplayMode {
|
|
||||||
#[serde(skip)]
|
|
||||||
pub local_user_id: LocalUserId,
|
|
||||||
pub score: bool,
|
|
||||||
pub upvotes: bool,
|
|
||||||
pub downvotes: bool,
|
|
||||||
pub upvote_percentage: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, derive_new::new)]
|
|
||||||
#[cfg_attr(feature = "full", derive(Insertable))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = local_user_vote_display_mode))]
|
|
||||||
pub struct LocalUserVoteDisplayModeInsertForm {
|
|
||||||
pub local_user_id: LocalUserId,
|
|
||||||
#[new(default)]
|
|
||||||
pub score: Option<bool>,
|
|
||||||
#[new(default)]
|
|
||||||
pub upvotes: Option<bool>,
|
|
||||||
#[new(default)]
|
|
||||||
pub downvotes: Option<bool>,
|
|
||||||
#[new(default)]
|
|
||||||
pub upvote_percentage: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
#[cfg_attr(feature = "full", derive(AsChangeset))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = local_user_vote_display_mode))]
|
|
||||||
pub struct LocalUserVoteDisplayModeUpdateForm {
|
|
||||||
pub score: Option<bool>,
|
|
||||||
pub upvotes: Option<bool>,
|
|
||||||
pub downvotes: Option<bool>,
|
|
||||||
pub upvote_percentage: Option<bool>,
|
|
||||||
}
|
|
|
@ -26,7 +26,6 @@ pub mod local_site;
|
||||||
pub mod local_site_rate_limit;
|
pub mod local_site_rate_limit;
|
||||||
pub mod local_site_url_blocklist;
|
pub mod local_site_url_blocklist;
|
||||||
pub mod local_user;
|
pub mod local_user;
|
||||||
pub mod local_user_vote_display_mode;
|
|
||||||
pub mod login_token;
|
pub mod login_token;
|
||||||
pub mod mod_log;
|
pub mod mod_log;
|
||||||
pub mod oauth_account;
|
pub mod oauth_account;
|
||||||
|
@ -37,6 +36,7 @@ pub mod person_block;
|
||||||
pub mod person_comment_mention;
|
pub mod person_comment_mention;
|
||||||
pub mod person_post_mention;
|
pub mod person_post_mention;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
|
pub mod post_actions;
|
||||||
pub mod post_report;
|
pub mod post_report;
|
||||||
pub mod private_message;
|
pub mod private_message;
|
||||||
pub mod private_message_report;
|
pub mod private_message_report;
|
||||||
|
|
|
@ -64,6 +64,12 @@ pub struct Person {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub ban_expires: Option<DateTime<Utc>>,
|
pub ban_expires: Option<DateTime<Utc>>,
|
||||||
pub instance_id: InstanceId,
|
pub instance_id: InstanceId,
|
||||||
|
pub post_count: i64,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub post_score: i64,
|
||||||
|
pub comment_count: i64,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub comment_score: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, derive_new::new)]
|
#[derive(Clone, derive_new::new)]
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
use crate::newtypes::{CommunityId, DbUrl, LanguageId, PersonId, PostId};
|
use crate::newtypes::{CommunityId, DbUrl, InstanceId, LanguageId, PersonId, PostId};
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use crate::schema::{post, post_actions};
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use diesel::{dsl, expression_methods::NullableExpressionMethods};
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use i_love_jesus::CursorKeysModule;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use ts_rs::TS;
|
use {
|
||||||
|
crate::schema::{post, post_actions},
|
||||||
|
diesel::{dsl, expression_methods::NullableExpressionMethods},
|
||||||
|
i_love_jesus::CursorKeysModule,
|
||||||
|
ts_rs::TS,
|
||||||
|
};
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
#[cfg_attr(
|
||||||
|
feature = "full",
|
||||||
|
derive(Queryable, Selectable, Identifiable, TS, CursorKeysModule)
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = post))]
|
#[cfg_attr(feature = "full", diesel(table_name = post))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
#[cfg_attr(feature = "full", cursor_keys_module(name = post_keys))]
|
||||||
/// A post.
|
/// A post.
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
pub id: PostId,
|
pub id: PostId,
|
||||||
|
@ -69,6 +72,28 @@ pub struct Post {
|
||||||
/// Time at which the post will be published. None means publish immediately.
|
/// Time at which the post will be published. None means publish immediately.
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub scheduled_publish_time: Option<DateTime<Utc>>,
|
pub scheduled_publish_time: Option<DateTime<Utc>>,
|
||||||
|
pub comments: i64,
|
||||||
|
pub score: i64,
|
||||||
|
pub upvotes: i64,
|
||||||
|
pub downvotes: i64,
|
||||||
|
#[serde(skip)]
|
||||||
|
/// A newest comment time, limited to 2 days, to prevent necrobumping
|
||||||
|
pub newest_comment_time_necro: DateTime<Utc>,
|
||||||
|
/// The time of the newest comment in the post.
|
||||||
|
pub newest_comment_time: DateTime<Utc>,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub hot_rank: f64,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub hot_rank_active: f64,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub controversy_rank: f64,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub instance_id: InstanceId,
|
||||||
|
/// A rank that amplifies smaller communities
|
||||||
|
#[serde(skip)]
|
||||||
|
pub scaled_rank: f64,
|
||||||
|
pub report_count: i16,
|
||||||
|
pub unresolved_report_count: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, derive_new::new)]
|
#[derive(Debug, Clone, derive_new::new)]
|
||||||
|
|
42
crates/db_schema/src/source/post_actions.rs
Normal file
42
crates/db_schema/src/source/post_actions.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use crate::newtypes::{PersonId, PostId};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
use {
|
||||||
|
crate::schema::post_actions,
|
||||||
|
diesel::{dsl, expression_methods::NullableExpressionMethods},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "full",
|
||||||
|
derive(Queryable, Selectable, Associations, Identifiable)
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = post_actions))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
/// Aggregate data for a person's post.
|
||||||
|
pub struct PostActions {
|
||||||
|
pub person_id: PersonId,
|
||||||
|
pub post_id: PostId,
|
||||||
|
/// The number of comments they've read on that post.
|
||||||
|
///
|
||||||
|
/// This is updated to the current post comment count every time they view a post.
|
||||||
|
#[cfg_attr(feature = "full", diesel(select_expression = post_actions::read_comments_amount.assume_not_null()))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<post_actions::read_comments_amount>))]
|
||||||
|
pub read_comments: i64,
|
||||||
|
#[cfg_attr(feature = "full", diesel(select_expression = post_actions::read_comments.assume_not_null()))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<post_actions::read_comments>))]
|
||||||
|
pub published: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = post_actions))]
|
||||||
|
pub struct PostActionsForm {
|
||||||
|
pub person_id: PersonId,
|
||||||
|
pub post_id: PostId,
|
||||||
|
#[cfg_attr(feature = "full", diesel(column_name = read_comments_amount))]
|
||||||
|
pub read_comments: i64,
|
||||||
|
}
|
|
@ -25,7 +25,6 @@ use lemmy_db_schema::{
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
comment_aggregates,
|
|
||||||
comment_reply,
|
comment_reply,
|
||||||
community,
|
community,
|
||||||
community_actions,
|
community_actions,
|
||||||
|
@ -39,7 +38,6 @@ use lemmy_db_schema::{
|
||||||
person_post_mention,
|
person_post_mention,
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
|
||||||
post_tag,
|
post_tag,
|
||||||
private_message,
|
private_message,
|
||||||
tag,
|
tag,
|
||||||
|
@ -104,10 +102,6 @@ impl InboxCombinedViewInternal {
|
||||||
.and(creator_local_user.field(local_user::admin).eq(true)),
|
.and(creator_local_user.field(local_user::admin).eq(true)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
|
||||||
let comment_aggregates_join =
|
|
||||||
comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id));
|
|
||||||
|
|
||||||
let image_details_join =
|
let image_details_join =
|
||||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||||
|
|
||||||
|
@ -163,8 +157,6 @@ impl InboxCombinedViewInternal {
|
||||||
.inner_join(person::table.on(item_creator_join))
|
.inner_join(person::table.on(item_creator_join))
|
||||||
.inner_join(recipient_join)
|
.inner_join(recipient_join)
|
||||||
.left_join(image_details_join)
|
.left_join(image_details_join)
|
||||||
.left_join(post_aggregates_join)
|
|
||||||
.left_join(comment_aggregates_join)
|
|
||||||
.left_join(creator_community_actions_join)
|
.left_join(creator_community_actions_join)
|
||||||
.left_join(local_user_join)
|
.left_join(local_user_join)
|
||||||
.left_join(creator_local_user_join)
|
.left_join(creator_local_user_join)
|
||||||
|
@ -289,10 +281,9 @@ impl InboxCombinedQuery {
|
||||||
comment_reply::all_columns.nullable(),
|
comment_reply::all_columns.nullable(),
|
||||||
person_comment_mention::all_columns.nullable(),
|
person_comment_mention::all_columns.nullable(),
|
||||||
person_post_mention::all_columns.nullable(),
|
person_post_mention::all_columns.nullable(),
|
||||||
post_aggregates::all_columns.nullable(),
|
|
||||||
coalesce(
|
coalesce(
|
||||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
post::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
post_aggregates::comments,
|
post::comments,
|
||||||
)
|
)
|
||||||
.nullable(),
|
.nullable(),
|
||||||
post_actions::saved.nullable(),
|
post_actions::saved.nullable(),
|
||||||
|
@ -306,7 +297,6 @@ impl InboxCombinedQuery {
|
||||||
post::all_columns.nullable(),
|
post::all_columns.nullable(),
|
||||||
community::all_columns.nullable(),
|
community::all_columns.nullable(),
|
||||||
comment::all_columns.nullable(),
|
comment::all_columns.nullable(),
|
||||||
comment_aggregates::all_columns.nullable(),
|
|
||||||
comment_actions::saved.nullable(),
|
comment_actions::saved.nullable(),
|
||||||
comment_actions::like_score.nullable(),
|
comment_actions::like_score.nullable(),
|
||||||
community_follower_select_subscribed_type(),
|
community_follower_select_subscribed_type(),
|
||||||
|
@ -428,17 +418,15 @@ impl InternalToCombinedView for InboxCombinedViewInternal {
|
||||||
// Use for a short alias
|
// Use for a short alias
|
||||||
let v = self;
|
let v = self;
|
||||||
|
|
||||||
if let (Some(comment_reply), Some(comment), Some(counts), Some(post), Some(community)) = (
|
if let (Some(comment_reply), Some(comment), Some(post), Some(community)) = (
|
||||||
v.comment_reply,
|
v.comment_reply,
|
||||||
v.comment.clone(),
|
v.comment.clone(),
|
||||||
v.comment_counts.clone(),
|
|
||||||
v.post.clone(),
|
v.post.clone(),
|
||||||
v.community.clone(),
|
v.community.clone(),
|
||||||
) {
|
) {
|
||||||
Some(InboxCombinedView::CommentReply(CommentReplyView {
|
Some(InboxCombinedView::CommentReply(CommentReplyView {
|
||||||
comment_reply,
|
comment_reply,
|
||||||
comment,
|
comment,
|
||||||
counts,
|
|
||||||
recipient: v.item_recipient,
|
recipient: v.item_recipient,
|
||||||
post,
|
post,
|
||||||
community,
|
community,
|
||||||
|
@ -453,16 +441,9 @@ impl InternalToCombinedView for InboxCombinedViewInternal {
|
||||||
banned_from_community: v.banned_from_community,
|
banned_from_community: v.banned_from_community,
|
||||||
can_mod: v.can_mod,
|
can_mod: v.can_mod,
|
||||||
}))
|
}))
|
||||||
} else if let (
|
} else if let (Some(person_comment_mention), Some(comment), Some(post), Some(community)) = (
|
||||||
Some(person_comment_mention),
|
|
||||||
Some(comment),
|
|
||||||
Some(counts),
|
|
||||||
Some(post),
|
|
||||||
Some(community),
|
|
||||||
) = (
|
|
||||||
v.person_comment_mention,
|
v.person_comment_mention,
|
||||||
v.comment,
|
v.comment,
|
||||||
v.comment_counts,
|
|
||||||
v.post.clone(),
|
v.post.clone(),
|
||||||
v.community.clone(),
|
v.community.clone(),
|
||||||
) {
|
) {
|
||||||
|
@ -470,7 +451,6 @@ impl InternalToCombinedView for InboxCombinedViewInternal {
|
||||||
PersonCommentMentionView {
|
PersonCommentMentionView {
|
||||||
person_comment_mention,
|
person_comment_mention,
|
||||||
comment,
|
comment,
|
||||||
counts,
|
|
||||||
recipient: v.item_recipient,
|
recipient: v.item_recipient,
|
||||||
post,
|
post,
|
||||||
community,
|
community,
|
||||||
|
@ -486,22 +466,14 @@ impl InternalToCombinedView for InboxCombinedViewInternal {
|
||||||
can_mod: v.can_mod,
|
can_mod: v.can_mod,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
} else if let (
|
} else if let (Some(person_post_mention), Some(post), Some(unread_comments), Some(community)) = (
|
||||||
Some(person_post_mention),
|
|
||||||
Some(post),
|
|
||||||
Some(counts),
|
|
||||||
Some(unread_comments),
|
|
||||||
Some(community),
|
|
||||||
) = (
|
|
||||||
v.person_post_mention,
|
v.person_post_mention,
|
||||||
v.post,
|
v.post,
|
||||||
v.post_counts,
|
|
||||||
v.post_unread_comments,
|
v.post_unread_comments,
|
||||||
v.community,
|
v.community,
|
||||||
) {
|
) {
|
||||||
Some(InboxCombinedView::PostMention(PersonPostMentionView {
|
Some(InboxCombinedView::PostMention(PersonPostMentionView {
|
||||||
person_post_mention,
|
person_post_mention,
|
||||||
counts,
|
|
||||||
post,
|
post,
|
||||||
community,
|
community,
|
||||||
recipient: v.item_recipient,
|
recipient: v.item_recipient,
|
||||||
|
|
|
@ -22,7 +22,6 @@ use lemmy_db_schema::{
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
comment_aggregates,
|
|
||||||
community,
|
community,
|
||||||
community_actions,
|
community_actions,
|
||||||
image_details,
|
image_details,
|
||||||
|
@ -32,7 +31,6 @@ use lemmy_db_schema::{
|
||||||
person_content_combined,
|
person_content_combined,
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
|
||||||
post_tag,
|
post_tag,
|
||||||
tag,
|
tag,
|
||||||
},
|
},
|
||||||
|
@ -113,11 +111,6 @@ impl PersonContentCombinedViewInternal {
|
||||||
.and(comment_actions::person_id.nullable().eq(my_person_id)),
|
.and(comment_actions::person_id.nullable().eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
|
||||||
|
|
||||||
let comment_aggregates_join = comment_aggregates::table
|
|
||||||
.on(person_content_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
|
|
||||||
|
|
||||||
let image_details_join =
|
let image_details_join =
|
||||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||||
|
|
||||||
|
@ -132,8 +125,6 @@ impl PersonContentCombinedViewInternal {
|
||||||
.left_join(community_actions_join)
|
.left_join(community_actions_join)
|
||||||
.left_join(post_actions_join)
|
.left_join(post_actions_join)
|
||||||
.left_join(person_actions_join)
|
.left_join(person_actions_join)
|
||||||
.inner_join(post_aggregates_join)
|
|
||||||
.left_join(comment_aggregates_join)
|
|
||||||
.left_join(comment_actions_join)
|
.left_join(comment_actions_join)
|
||||||
.left_join(image_details_join)
|
.left_join(image_details_join)
|
||||||
}
|
}
|
||||||
|
@ -213,10 +204,9 @@ impl PersonContentCombinedQuery {
|
||||||
.filter(item_creator.eq(self.creator_id))
|
.filter(item_creator.eq(self.creator_id))
|
||||||
.select((
|
.select((
|
||||||
// Post-specific
|
// Post-specific
|
||||||
post_aggregates::all_columns,
|
|
||||||
coalesce(
|
coalesce(
|
||||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
post::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
post_aggregates::comments,
|
post::comments,
|
||||||
),
|
),
|
||||||
post_actions::saved.nullable(),
|
post_actions::saved.nullable(),
|
||||||
post_actions::read.nullable().is_not_null(),
|
post_actions::read.nullable().is_not_null(),
|
||||||
|
@ -226,7 +216,6 @@ impl PersonContentCombinedQuery {
|
||||||
post_tags,
|
post_tags,
|
||||||
// Comment-specific
|
// Comment-specific
|
||||||
comment::all_columns.nullable(),
|
comment::all_columns.nullable(),
|
||||||
comment_aggregates::all_columns.nullable(),
|
|
||||||
comment_actions::saved.nullable(),
|
comment_actions::saved.nullable(),
|
||||||
comment_actions::like_score.nullable(),
|
comment_actions::like_score.nullable(),
|
||||||
// Shared
|
// Shared
|
||||||
|
@ -297,10 +286,9 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal {
|
||||||
// Use for a short alias
|
// Use for a short alias
|
||||||
let v = self;
|
let v = self;
|
||||||
|
|
||||||
if let (Some(comment), Some(counts)) = (v.comment, v.comment_counts) {
|
if let Some(comment) = v.comment {
|
||||||
Some(PersonContentCombinedView::Comment(CommentView {
|
Some(PersonContentCombinedView::Comment(CommentView {
|
||||||
comment,
|
comment,
|
||||||
counts,
|
|
||||||
post: v.post,
|
post: v.post,
|
||||||
community: v.community,
|
community: v.community,
|
||||||
creator: v.item_creator,
|
creator: v.item_creator,
|
||||||
|
@ -319,7 +307,6 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal {
|
||||||
post: v.post,
|
post: v.post,
|
||||||
community: v.community,
|
community: v.community,
|
||||||
unread_comments: v.post_unread_comments,
|
unread_comments: v.post_unread_comments,
|
||||||
counts: v.post_counts,
|
|
||||||
creator: v.item_creator,
|
creator: v.item_creator,
|
||||||
creator_banned_from_community: v.item_creator_banned_from_community,
|
creator_banned_from_community: v.item_creator_banned_from_community,
|
||||||
creator_is_moderator: v.item_creator_is_moderator,
|
creator_is_moderator: v.item_creator_is_moderator,
|
||||||
|
|
|
@ -22,7 +22,6 @@ use lemmy_db_schema::{
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
comment_aggregates,
|
|
||||||
community,
|
community,
|
||||||
community_actions,
|
community_actions,
|
||||||
image_details,
|
image_details,
|
||||||
|
@ -32,7 +31,6 @@ use lemmy_db_schema::{
|
||||||
person_saved_combined,
|
person_saved_combined,
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
|
||||||
post_tag,
|
post_tag,
|
||||||
tag,
|
tag,
|
||||||
},
|
},
|
||||||
|
@ -154,11 +152,6 @@ impl PersonSavedCombinedViewInternal {
|
||||||
.and(comment_actions::person_id.eq(my_person_id)),
|
.and(comment_actions::person_id.eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
|
||||||
|
|
||||||
let comment_aggregates_join = comment_aggregates::table
|
|
||||||
.on(person_saved_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
|
|
||||||
|
|
||||||
let image_details_join =
|
let image_details_join =
|
||||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||||
|
|
||||||
|
@ -173,8 +166,6 @@ impl PersonSavedCombinedViewInternal {
|
||||||
.left_join(community_actions_join)
|
.left_join(community_actions_join)
|
||||||
.left_join(post_actions_join)
|
.left_join(post_actions_join)
|
||||||
.left_join(person_actions_join)
|
.left_join(person_actions_join)
|
||||||
.inner_join(post_aggregates_join)
|
|
||||||
.left_join(comment_aggregates_join)
|
|
||||||
.left_join(comment_actions_join)
|
.left_join(comment_actions_join)
|
||||||
.left_join(image_details_join)
|
.left_join(image_details_join)
|
||||||
}
|
}
|
||||||
|
@ -203,10 +194,9 @@ impl PersonSavedCombinedQuery {
|
||||||
.filter(person_saved_combined::person_id.eq(my_person_id))
|
.filter(person_saved_combined::person_id.eq(my_person_id))
|
||||||
.select((
|
.select((
|
||||||
// Post-specific
|
// Post-specific
|
||||||
post_aggregates::all_columns,
|
|
||||||
coalesce(
|
coalesce(
|
||||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
post::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
post_aggregates::comments,
|
post::comments,
|
||||||
),
|
),
|
||||||
post_actions::saved.nullable(),
|
post_actions::saved.nullable(),
|
||||||
post_actions::read.nullable().is_not_null(),
|
post_actions::read.nullable().is_not_null(),
|
||||||
|
@ -216,7 +206,6 @@ impl PersonSavedCombinedQuery {
|
||||||
post_tags,
|
post_tags,
|
||||||
// Comment-specific
|
// Comment-specific
|
||||||
comment::all_columns.nullable(),
|
comment::all_columns.nullable(),
|
||||||
comment_aggregates::all_columns.nullable(),
|
|
||||||
comment_actions::saved.nullable(),
|
comment_actions::saved.nullable(),
|
||||||
comment_actions::like_score.nullable(),
|
comment_actions::like_score.nullable(),
|
||||||
// Shared
|
// Shared
|
||||||
|
@ -285,10 +274,9 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal {
|
||||||
// Use for a short alias
|
// Use for a short alias
|
||||||
let v = self;
|
let v = self;
|
||||||
|
|
||||||
if let (Some(comment), Some(counts)) = (v.comment, v.comment_counts) {
|
if let Some(comment) = v.comment {
|
||||||
Some(PersonSavedCombinedView::Comment(CommentView {
|
Some(PersonSavedCombinedView::Comment(CommentView {
|
||||||
comment,
|
comment,
|
||||||
counts,
|
|
||||||
post: v.post,
|
post: v.post,
|
||||||
community: v.community,
|
community: v.community,
|
||||||
creator: v.item_creator,
|
creator: v.item_creator,
|
||||||
|
@ -307,7 +295,6 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal {
|
||||||
post: v.post,
|
post: v.post,
|
||||||
community: v.community,
|
community: v.community,
|
||||||
unread_comments: v.post_unread_comments,
|
unread_comments: v.post_unread_comments,
|
||||||
counts: v.post_counts,
|
|
||||||
creator: v.item_creator,
|
creator: v.item_creator,
|
||||||
creator_banned_from_community: v.item_creator_banned_from_community,
|
creator_banned_from_community: v.item_creator_banned_from_community,
|
||||||
creator_is_moderator: v.item_creator_is_moderator,
|
creator_is_moderator: v.item_creator_is_moderator,
|
||||||
|
@ -341,7 +328,6 @@ mod tests {
|
||||||
community::{Community, CommunityInsertForm},
|
community::{Community, CommunityInsertForm},
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
local_user::{LocalUser, LocalUserInsertForm},
|
local_user::{LocalUser, LocalUserInsertForm},
|
||||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
post::{Post, PostInsertForm, PostSaved, PostSavedForm},
|
post::{Post, PostInsertForm, PostSaved, PostSavedForm},
|
||||||
},
|
},
|
||||||
|
@ -371,9 +357,7 @@ mod tests {
|
||||||
let timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
let timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
||||||
let timmy_view = LocalUserView {
|
let timmy_view = LocalUserView {
|
||||||
local_user: timmy_local_user,
|
local_user: timmy_local_user,
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
|
||||||
person: timmy.clone(),
|
person: timmy.clone(),
|
||||||
counts: Default::default(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let sara_form = PersonInsertForm::test_form(instance.id, "sara_pcv");
|
let sara_form = PersonInsertForm::test_form(instance.id, "sara_pcv");
|
||||||
|
|
|
@ -27,18 +27,15 @@ use lemmy_db_schema::{
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
comment_aggregates,
|
|
||||||
comment_report,
|
comment_report,
|
||||||
community,
|
community,
|
||||||
community_actions,
|
community_actions,
|
||||||
community_aggregates,
|
|
||||||
community_report,
|
community_report,
|
||||||
local_user,
|
local_user,
|
||||||
person,
|
person,
|
||||||
person_actions,
|
person_actions,
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
|
||||||
post_report,
|
post_report,
|
||||||
private_message,
|
private_message,
|
||||||
private_message_report,
|
private_message_report,
|
||||||
|
@ -138,15 +135,6 @@ impl ReportCombinedViewInternal {
|
||||||
.and(comment_actions::person_id.eq(my_person_id)),
|
.and(comment_actions::person_id.eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let post_aggregates_join =
|
|
||||||
post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id));
|
|
||||||
|
|
||||||
let comment_aggregates_join =
|
|
||||||
comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id));
|
|
||||||
|
|
||||||
let community_aggregates_join = community_aggregates::table
|
|
||||||
.on(community_report::community_id.eq(community_aggregates::community_id));
|
|
||||||
|
|
||||||
report_combined::table
|
report_combined::table
|
||||||
.left_join(post_report::table)
|
.left_join(post_report::table)
|
||||||
.left_join(comment_report::table)
|
.left_join(comment_report::table)
|
||||||
|
@ -164,9 +152,6 @@ impl ReportCombinedViewInternal {
|
||||||
.left_join(community_actions_join)
|
.left_join(community_actions_join)
|
||||||
.left_join(post_actions_join)
|
.left_join(post_actions_join)
|
||||||
.left_join(person_actions_join)
|
.left_join(person_actions_join)
|
||||||
.left_join(post_aggregates_join)
|
|
||||||
.left_join(comment_aggregates_join)
|
|
||||||
.left_join(community_aggregates_join)
|
|
||||||
.left_join(comment_actions_join)
|
.left_join(comment_actions_join)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,10 +259,9 @@ impl ReportCombinedQuery {
|
||||||
// Post-specific
|
// Post-specific
|
||||||
post_report::all_columns.nullable(),
|
post_report::all_columns.nullable(),
|
||||||
post::all_columns.nullable(),
|
post::all_columns.nullable(),
|
||||||
post_aggregates::all_columns.nullable(),
|
|
||||||
coalesce(
|
coalesce(
|
||||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
post::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
post_aggregates::comments,
|
post::comments,
|
||||||
)
|
)
|
||||||
.nullable(),
|
.nullable(),
|
||||||
post_actions::saved.nullable(),
|
post_actions::saved.nullable(),
|
||||||
|
@ -287,7 +271,6 @@ impl ReportCombinedQuery {
|
||||||
// Comment-specific
|
// Comment-specific
|
||||||
comment_report::all_columns.nullable(),
|
comment_report::all_columns.nullable(),
|
||||||
comment::all_columns.nullable(),
|
comment::all_columns.nullable(),
|
||||||
comment_aggregates::all_columns.nullable(),
|
|
||||||
comment_actions::saved.nullable(),
|
comment_actions::saved.nullable(),
|
||||||
comment_actions::like_score.nullable(),
|
comment_actions::like_score.nullable(),
|
||||||
// Private-message-specific
|
// Private-message-specific
|
||||||
|
@ -295,7 +278,6 @@ impl ReportCombinedQuery {
|
||||||
private_message::all_columns.nullable(),
|
private_message::all_columns.nullable(),
|
||||||
// Community-specific
|
// Community-specific
|
||||||
community_report::all_columns.nullable(),
|
community_report::all_columns.nullable(),
|
||||||
community_aggregates::all_columns.nullable(),
|
|
||||||
// Shared
|
// Shared
|
||||||
person::all_columns,
|
person::all_columns,
|
||||||
aliases::person1.fields(person::all_columns.nullable()),
|
aliases::person1.fields(person::all_columns.nullable()),
|
||||||
|
@ -437,14 +419,12 @@ impl InternalToCombinedView for ReportCombinedViewInternal {
|
||||||
Some(post),
|
Some(post),
|
||||||
Some(community),
|
Some(community),
|
||||||
Some(unread_comments),
|
Some(unread_comments),
|
||||||
Some(counts),
|
|
||||||
Some(post_creator),
|
Some(post_creator),
|
||||||
) = (
|
) = (
|
||||||
v.post_report,
|
v.post_report,
|
||||||
v.post.clone(),
|
v.post.clone(),
|
||||||
v.community.clone(),
|
v.community.clone(),
|
||||||
v.post_unread_comments,
|
v.post_unread_comments,
|
||||||
v.post_counts,
|
|
||||||
v.item_creator.clone(),
|
v.item_creator.clone(),
|
||||||
) {
|
) {
|
||||||
Some(ReportCombinedView::Post(PostReportView {
|
Some(ReportCombinedView::Post(PostReportView {
|
||||||
|
@ -452,7 +432,6 @@ impl InternalToCombinedView for ReportCombinedViewInternal {
|
||||||
post,
|
post,
|
||||||
community,
|
community,
|
||||||
unread_comments,
|
unread_comments,
|
||||||
counts,
|
|
||||||
creator: v.report_creator,
|
creator: v.report_creator,
|
||||||
post_creator,
|
post_creator,
|
||||||
creator_banned_from_community: v.item_creator_banned_from_community,
|
creator_banned_from_community: v.item_creator_banned_from_community,
|
||||||
|
@ -469,14 +448,12 @@ impl InternalToCombinedView for ReportCombinedViewInternal {
|
||||||
} else if let (
|
} else if let (
|
||||||
Some(comment_report),
|
Some(comment_report),
|
||||||
Some(comment),
|
Some(comment),
|
||||||
Some(counts),
|
|
||||||
Some(post),
|
Some(post),
|
||||||
Some(community),
|
Some(community),
|
||||||
Some(comment_creator),
|
Some(comment_creator),
|
||||||
) = (
|
) = (
|
||||||
v.comment_report,
|
v.comment_report,
|
||||||
v.comment,
|
v.comment,
|
||||||
v.comment_counts,
|
|
||||||
v.post,
|
v.post,
|
||||||
v.community.clone(),
|
v.community.clone(),
|
||||||
v.item_creator.clone(),
|
v.item_creator.clone(),
|
||||||
|
@ -484,7 +461,6 @@ impl InternalToCombinedView for ReportCombinedViewInternal {
|
||||||
Some(ReportCombinedView::Comment(CommentReportView {
|
Some(ReportCombinedView::Comment(CommentReportView {
|
||||||
comment_report,
|
comment_report,
|
||||||
comment,
|
comment,
|
||||||
counts,
|
|
||||||
post,
|
post,
|
||||||
community,
|
community,
|
||||||
creator: v.report_creator,
|
creator: v.report_creator,
|
||||||
|
@ -513,14 +489,11 @@ impl InternalToCombinedView for ReportCombinedViewInternal {
|
||||||
resolver: v.resolver,
|
resolver: v.resolver,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
} else if let (Some(community), Some(community_report), Some(counts)) =
|
} else if let (Some(community), Some(community_report)) = (v.community, v.community_report) {
|
||||||
(v.community, v.community_report, v.community_counts)
|
|
||||||
{
|
|
||||||
Some(ReportCombinedView::Community(CommunityReportView {
|
Some(ReportCombinedView::Community(CommunityReportView {
|
||||||
community_report,
|
community_report,
|
||||||
community,
|
community,
|
||||||
creator: v.report_creator,
|
creator: v.report_creator,
|
||||||
counts,
|
|
||||||
subscribed: v.subscribed,
|
subscribed: v.subscribed,
|
||||||
resolver: v.resolver,
|
resolver: v.resolver,
|
||||||
}))
|
}))
|
||||||
|
@ -548,7 +521,6 @@ mod tests {
|
||||||
use diesel::{update, ExpressionMethods, QueryDsl};
|
use diesel::{update, ExpressionMethods, QueryDsl};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::{CommentAggregates, PostAggregates},
|
|
||||||
assert_length,
|
assert_length,
|
||||||
schema::report_combined,
|
schema::report_combined,
|
||||||
source::{
|
source::{
|
||||||
|
@ -558,7 +530,6 @@ mod tests {
|
||||||
community_report::{CommunityReport, CommunityReportForm},
|
community_report::{CommunityReport, CommunityReportForm},
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
local_user::{LocalUser, LocalUserInsertForm},
|
local_user::{LocalUser, LocalUserInsertForm},
|
||||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
post::{Post, PostInsertForm},
|
post::{Post, PostInsertForm},
|
||||||
post_report::{PostReport, PostReportForm},
|
post_report::{PostReport, PostReportForm},
|
||||||
|
@ -595,9 +566,7 @@ mod tests {
|
||||||
let timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
let timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
||||||
let timmy_view = LocalUserView {
|
let timmy_view = LocalUserView {
|
||||||
local_user: timmy_local_user,
|
local_user: timmy_local_user,
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
|
||||||
person: inserted_timmy.clone(),
|
person: inserted_timmy.clone(),
|
||||||
counts: Default::default(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make an admin, to be able to see private message reports.
|
// Make an admin, to be able to see private message reports.
|
||||||
|
@ -607,9 +576,7 @@ mod tests {
|
||||||
let admin_local_user = LocalUser::create(pool, &admin_local_user_form, vec![]).await?;
|
let admin_local_user = LocalUser::create(pool, &admin_local_user_form, vec![]).await?;
|
||||||
let admin_view = LocalUserView {
|
let admin_view = LocalUserView {
|
||||||
local_user: admin_local_user,
|
local_user: admin_local_user,
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
|
||||||
person: inserted_admin.clone(),
|
person: inserted_admin.clone(),
|
||||||
counts: Default::default(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let sara_form = PersonInsertForm::test_form(inserted_instance.id, "sara_rcv");
|
let sara_form = PersonInsertForm::test_form(inserted_instance.id, "sara_rcv");
|
||||||
|
@ -947,14 +914,14 @@ mod tests {
|
||||||
PostReportView::read(pool, inserted_jessica_report.id, data.timmy.id).await?;
|
PostReportView::read(pool, inserted_jessica_report.id, data.timmy.id).await?;
|
||||||
|
|
||||||
// Make sure the triggers are reading the aggregates correctly.
|
// Make sure the triggers are reading the aggregates correctly.
|
||||||
let agg_1 = PostAggregates::read(pool, data.post.id).await?;
|
let agg_1 = Post::read(pool, data.post.id).await?;
|
||||||
let agg_2 = PostAggregates::read(pool, data.post_2.id).await?;
|
let agg_2 = Post::read(pool, data.post_2.id).await?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
read_jessica_report_view.post_report,
|
read_jessica_report_view.post_report,
|
||||||
inserted_jessica_report
|
inserted_jessica_report
|
||||||
);
|
);
|
||||||
assert_eq!(read_jessica_report_view.post, data.post_2);
|
assert_eq!(read_jessica_report_view.post.id, data.post_2.id);
|
||||||
assert_eq!(read_jessica_report_view.community.id, data.community.id);
|
assert_eq!(read_jessica_report_view.community.id, data.community.id);
|
||||||
assert_eq!(read_jessica_report_view.creator.id, data.jessica.id);
|
assert_eq!(read_jessica_report_view.creator.id, data.jessica.id);
|
||||||
assert_eq!(read_jessica_report_view.post_creator.id, data.timmy.id);
|
assert_eq!(read_jessica_report_view.post_creator.id, data.timmy.id);
|
||||||
|
@ -1008,12 +975,12 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure the unresolved_post report got decremented in the trigger
|
// Make sure the unresolved_post report got decremented in the trigger
|
||||||
let agg_2 = PostAggregates::read(pool, data.post_2.id).await?;
|
let agg_2 = Post::read(pool, data.post_2.id).await?;
|
||||||
assert_eq!(agg_2.report_count, 1);
|
assert_eq!(agg_2.report_count, 1);
|
||||||
assert_eq!(agg_2.unresolved_report_count, 0);
|
assert_eq!(agg_2.unresolved_report_count, 0);
|
||||||
|
|
||||||
// Make sure the other unresolved report isn't changed
|
// Make sure the other unresolved report isn't changed
|
||||||
let agg_1 = PostAggregates::read(pool, data.post.id).await?;
|
let agg_1 = Post::read(pool, data.post.id).await?;
|
||||||
assert_eq!(agg_1.report_count, 1);
|
assert_eq!(agg_1.report_count, 1);
|
||||||
assert_eq!(agg_1.unresolved_report_count, 1);
|
assert_eq!(agg_1.unresolved_report_count, 1);
|
||||||
|
|
||||||
|
@ -1072,12 +1039,12 @@ mod tests {
|
||||||
|
|
||||||
let inserted_jessica_report = CommentReport::report(pool, &jessica_report_form).await?;
|
let inserted_jessica_report = CommentReport::report(pool, &jessica_report_form).await?;
|
||||||
|
|
||||||
let agg = CommentAggregates::read(pool, data.comment.id).await?;
|
let comment = Comment::read(pool, data.comment.id).await?;
|
||||||
assert_eq!(agg.report_count, 2);
|
assert_eq!(comment.report_count, 2);
|
||||||
|
|
||||||
let read_jessica_report_view =
|
let read_jessica_report_view =
|
||||||
CommentReportView::read(pool, inserted_jessica_report.id, data.timmy.id).await?;
|
CommentReportView::read(pool, inserted_jessica_report.id, data.timmy.id).await?;
|
||||||
assert_eq!(read_jessica_report_view.counts.unresolved_report_count, 2);
|
assert_eq!(read_jessica_report_view.comment.unresolved_report_count, 2);
|
||||||
|
|
||||||
// Do a batch read of timmys reports
|
// Do a batch read of timmys reports
|
||||||
let reports = ReportCombinedQuery::default()
|
let reports = ReportCombinedQuery::default()
|
||||||
|
@ -1340,7 +1307,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
CommentReport::report(pool, &timmy_report_form).await?;
|
CommentReport::report(pool, &timmy_report_form).await?;
|
||||||
|
|
||||||
let agg = CommentAggregates::read(pool, data.comment.id).await?;
|
let agg = Comment::read(pool, data.comment.id).await?;
|
||||||
assert_eq!(agg.report_count, 2);
|
assert_eq!(agg.report_count, 2);
|
||||||
|
|
||||||
// Do a batch read of timmys reports, it should only show his own
|
// Do a batch read of timmys reports, it should only show his own
|
||||||
|
|
|
@ -26,18 +26,14 @@ use lemmy_db_schema::{
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
comment_aggregates,
|
|
||||||
community,
|
community,
|
||||||
community_actions,
|
community_actions,
|
||||||
community_aggregates,
|
|
||||||
image_details,
|
image_details,
|
||||||
local_user,
|
local_user,
|
||||||
person,
|
person,
|
||||||
person_actions,
|
person_actions,
|
||||||
person_aggregates,
|
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
|
||||||
post_tag,
|
post_tag,
|
||||||
search_combined,
|
search_combined,
|
||||||
tag,
|
tag,
|
||||||
|
@ -147,20 +143,9 @@ impl SearchCombinedViewInternal {
|
||||||
.and(comment_actions::person_id.nullable().eq(my_person_id)),
|
.and(comment_actions::person_id.nullable().eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
|
||||||
|
|
||||||
let comment_aggregates_join = comment_aggregates::table
|
|
||||||
.on(search_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
|
|
||||||
|
|
||||||
let community_aggregates_join = community_aggregates::table
|
|
||||||
.on(search_combined::community_id.eq(community_aggregates::community_id.nullable()));
|
|
||||||
|
|
||||||
let image_details_join =
|
let image_details_join =
|
||||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||||
|
|
||||||
let person_aggregates_join = person_aggregates::table
|
|
||||||
.on(search_combined::person_id.eq(person_aggregates::person_id.nullable()));
|
|
||||||
|
|
||||||
search_combined::table
|
search_combined::table
|
||||||
.left_join(comment_join)
|
.left_join(comment_join)
|
||||||
.left_join(post_join)
|
.left_join(post_join)
|
||||||
|
@ -172,10 +157,6 @@ impl SearchCombinedViewInternal {
|
||||||
.left_join(community_actions_join)
|
.left_join(community_actions_join)
|
||||||
.left_join(post_actions_join)
|
.left_join(post_actions_join)
|
||||||
.left_join(person_actions_join)
|
.left_join(person_actions_join)
|
||||||
.left_join(person_aggregates_join)
|
|
||||||
.left_join(post_aggregates_join)
|
|
||||||
.left_join(comment_aggregates_join)
|
|
||||||
.left_join(community_aggregates_join)
|
|
||||||
.left_join(comment_actions_join)
|
.left_join(comment_actions_join)
|
||||||
.left_join(image_details_join)
|
.left_join(image_details_join)
|
||||||
}
|
}
|
||||||
|
@ -259,10 +240,9 @@ impl SearchCombinedQuery {
|
||||||
.select((
|
.select((
|
||||||
// Post-specific
|
// Post-specific
|
||||||
post::all_columns.nullable(),
|
post::all_columns.nullable(),
|
||||||
post_aggregates::all_columns.nullable(),
|
|
||||||
coalesce(
|
coalesce(
|
||||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
post::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
post_aggregates::comments,
|
post::comments,
|
||||||
)
|
)
|
||||||
.nullable(),
|
.nullable(),
|
||||||
post_actions::saved.nullable(),
|
post_actions::saved.nullable(),
|
||||||
|
@ -273,16 +253,12 @@ impl SearchCombinedQuery {
|
||||||
post_tags,
|
post_tags,
|
||||||
// Comment-specific
|
// Comment-specific
|
||||||
comment::all_columns.nullable(),
|
comment::all_columns.nullable(),
|
||||||
comment_aggregates::all_columns.nullable(),
|
|
||||||
comment_actions::saved.nullable(),
|
comment_actions::saved.nullable(),
|
||||||
comment_actions::like_score.nullable(),
|
comment_actions::like_score.nullable(),
|
||||||
// Community-specific
|
// Community-specific
|
||||||
community::all_columns.nullable(),
|
community::all_columns.nullable(),
|
||||||
community_aggregates::all_columns.nullable(),
|
|
||||||
community_actions::blocked.nullable().is_not_null(),
|
community_actions::blocked.nullable().is_not_null(),
|
||||||
community_follower_select_subscribed_type(),
|
community_follower_select_subscribed_type(),
|
||||||
// Person
|
|
||||||
person_aggregates::all_columns.nullable(),
|
|
||||||
// // Shared
|
// // Shared
|
||||||
person::all_columns.nullable(),
|
person::all_columns.nullable(),
|
||||||
local_user::admin.nullable().is_not_null(),
|
local_user::admin.nullable().is_not_null(),
|
||||||
|
@ -440,16 +416,14 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
|
||||||
// Use for a short alias
|
// Use for a short alias
|
||||||
let v = self;
|
let v = self;
|
||||||
|
|
||||||
if let (Some(comment), Some(counts), Some(creator), Some(post), Some(community)) = (
|
if let (Some(comment), Some(creator), Some(post), Some(community)) = (
|
||||||
v.comment,
|
v.comment,
|
||||||
v.comment_counts,
|
|
||||||
v.item_creator.clone(),
|
v.item_creator.clone(),
|
||||||
v.post.clone(),
|
v.post.clone(),
|
||||||
v.community.clone(),
|
v.community.clone(),
|
||||||
) {
|
) {
|
||||||
Some(SearchCombinedView::Comment(CommentView {
|
Some(SearchCombinedView::Comment(CommentView {
|
||||||
comment,
|
comment,
|
||||||
counts,
|
|
||||||
post,
|
post,
|
||||||
community,
|
community,
|
||||||
creator,
|
creator,
|
||||||
|
@ -463,15 +437,8 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
|
||||||
banned_from_community: v.banned_from_community,
|
banned_from_community: v.banned_from_community,
|
||||||
can_mod: v.can_mod,
|
can_mod: v.can_mod,
|
||||||
}))
|
}))
|
||||||
} else if let (
|
} else if let (Some(post), Some(creator), Some(community), Some(unread_comments)) = (
|
||||||
Some(post),
|
|
||||||
Some(counts),
|
|
||||||
Some(creator),
|
|
||||||
Some(community),
|
|
||||||
Some(unread_comments),
|
|
||||||
) = (
|
|
||||||
v.post,
|
v.post,
|
||||||
v.post_counts,
|
|
||||||
v.item_creator.clone(),
|
v.item_creator.clone(),
|
||||||
v.community.clone(),
|
v.community.clone(),
|
||||||
v.post_unread_comments,
|
v.post_unread_comments,
|
||||||
|
@ -479,7 +446,6 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
|
||||||
Some(SearchCombinedView::Post(PostView {
|
Some(SearchCombinedView::Post(PostView {
|
||||||
post,
|
post,
|
||||||
community,
|
community,
|
||||||
counts,
|
|
||||||
unread_comments,
|
unread_comments,
|
||||||
creator,
|
creator,
|
||||||
creator_banned_from_community: v.item_creator_banned_from_community,
|
creator_banned_from_community: v.item_creator_banned_from_community,
|
||||||
|
@ -496,19 +462,17 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
|
||||||
tags: v.post_tags,
|
tags: v.post_tags,
|
||||||
can_mod: v.can_mod,
|
can_mod: v.can_mod,
|
||||||
}))
|
}))
|
||||||
} else if let (Some(community), Some(counts)) = (v.community, v.community_counts) {
|
} else if let Some(community) = v.community {
|
||||||
Some(SearchCombinedView::Community(CommunityView {
|
Some(SearchCombinedView::Community(CommunityView {
|
||||||
community,
|
community,
|
||||||
counts,
|
|
||||||
subscribed: v.subscribed,
|
subscribed: v.subscribed,
|
||||||
blocked: v.community_blocked,
|
blocked: v.community_blocked,
|
||||||
banned_from_community: v.banned_from_community,
|
banned_from_community: v.banned_from_community,
|
||||||
can_mod: v.can_mod,
|
can_mod: v.can_mod,
|
||||||
}))
|
}))
|
||||||
} else if let (Some(person), Some(counts)) = (v.item_creator, v.item_creator_counts) {
|
} else if let Some(person) = v.item_creator {
|
||||||
Some(SearchCombinedView::Person(PersonView {
|
Some(SearchCombinedView::Person(PersonView {
|
||||||
person,
|
person,
|
||||||
counts,
|
|
||||||
is_admin: v.item_creator_is_admin,
|
is_admin: v.item_creator_is_admin,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
@ -532,7 +496,6 @@ mod tests {
|
||||||
community::{Community, CommunityInsertForm},
|
community::{Community, CommunityInsertForm},
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
local_user::{LocalUser, LocalUserInsertForm},
|
local_user::{LocalUser, LocalUserInsertForm},
|
||||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm},
|
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm},
|
||||||
},
|
},
|
||||||
|
@ -573,9 +536,7 @@ mod tests {
|
||||||
let timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
let timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
||||||
let timmy_view = LocalUserView {
|
let timmy_view = LocalUserView {
|
||||||
local_user: timmy_local_user,
|
local_user: timmy_local_user,
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
|
||||||
person: timmy.clone(),
|
person: timmy.clone(),
|
||||||
counts: Default::default(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let community_form = CommunityInsertForm {
|
let community_form = CommunityInsertForm {
|
||||||
|
|
|
@ -21,7 +21,6 @@ use lemmy_db_schema::{
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
comment_aggregates,
|
|
||||||
community,
|
community,
|
||||||
community_actions,
|
community_actions,
|
||||||
instance_actions,
|
instance_actions,
|
||||||
|
@ -51,7 +50,7 @@ impl CommentView {
|
||||||
|
|
||||||
let comment_actions_join = comment_actions::table.on(
|
let comment_actions_join = comment_actions::table.on(
|
||||||
comment_actions::comment_id
|
comment_actions::comment_id
|
||||||
.eq(comment_aggregates::comment_id)
|
.eq(comment::id)
|
||||||
.and(comment_actions::person_id.nullable().eq(my_person_id)),
|
.and(comment_actions::person_id.nullable().eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -84,7 +83,6 @@ impl CommentView {
|
||||||
.inner_join(person::table)
|
.inner_join(person::table)
|
||||||
.inner_join(post::table)
|
.inner_join(post::table)
|
||||||
.inner_join(community_join)
|
.inner_join(community_join)
|
||||||
.inner_join(comment_aggregates::table)
|
|
||||||
.left_join(community_actions_join)
|
.left_join(community_actions_join)
|
||||||
.left_join(comment_actions_join)
|
.left_join(comment_actions_join)
|
||||||
.left_join(person_actions_join)
|
.left_join(person_actions_join)
|
||||||
|
@ -134,7 +132,6 @@ impl CommentView {
|
||||||
CommentSlimView {
|
CommentSlimView {
|
||||||
comment: self.comment,
|
comment: self.comment,
|
||||||
creator: self.creator,
|
creator: self.creator,
|
||||||
counts: self.counts,
|
|
||||||
creator_banned_from_community: self.creator_banned_from_community,
|
creator_banned_from_community: self.creator_banned_from_community,
|
||||||
banned_from_community: self.banned_from_community,
|
banned_from_community: self.banned_from_community,
|
||||||
creator_is_moderator: self.creator_is_moderator,
|
creator_is_moderator: self.creator_is_moderator,
|
||||||
|
@ -296,21 +293,18 @@ impl CommentQuery<'_> {
|
||||||
|
|
||||||
query = match o.sort.unwrap_or(CommentSortType::Hot) {
|
query = match o.sort.unwrap_or(CommentSortType::Hot) {
|
||||||
CommentSortType::Hot => query
|
CommentSortType::Hot => query
|
||||||
.then_order_by(comment_aggregates::hot_rank.desc())
|
.then_order_by(comment::hot_rank.desc())
|
||||||
.then_order_by(comment_aggregates::score.desc()),
|
.then_order_by(comment::score.desc()),
|
||||||
CommentSortType::Controversial => {
|
CommentSortType::Controversial => query.then_order_by(comment::controversy_rank.desc()),
|
||||||
query.then_order_by(comment_aggregates::controversy_rank.desc())
|
|
||||||
}
|
|
||||||
CommentSortType::New => query.then_order_by(comment::published.desc()),
|
CommentSortType::New => query.then_order_by(comment::published.desc()),
|
||||||
CommentSortType::Old => query.then_order_by(comment::published.asc()),
|
CommentSortType::Old => query.then_order_by(comment::published.asc()),
|
||||||
CommentSortType::Top => query.then_order_by(comment_aggregates::score.desc()),
|
CommentSortType::Top => query.then_order_by(comment::score.desc()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Filter by the time range
|
// Filter by the time range
|
||||||
if let Some(time_range_seconds) = o.time_range_seconds {
|
if let Some(time_range_seconds) = o.time_range_seconds {
|
||||||
query = query.filter(
|
query =
|
||||||
comment_aggregates::published.gt(now() - seconds_to_pg_interval(time_range_seconds)),
|
query.filter(comment::published.gt(now() - seconds_to_pg_interval(time_range_seconds)));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = query
|
let res = query
|
||||||
|
@ -332,10 +326,9 @@ mod tests {
|
||||||
structs::LocalUserView,
|
structs::LocalUserView,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::CommentAggregates,
|
|
||||||
assert_length,
|
assert_length,
|
||||||
impls::actor_language::UNDETERMINED_ID,
|
impls::actor_language::UNDETERMINED_ID,
|
||||||
newtypes::LanguageId,
|
newtypes::{CommentId, LanguageId},
|
||||||
source::{
|
source::{
|
||||||
actor_language::LocalUserLanguage,
|
actor_language::LocalUserLanguage,
|
||||||
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
|
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
|
||||||
|
@ -354,7 +347,6 @@ mod tests {
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
language::Language,
|
language::Language,
|
||||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
person_block::{PersonBlock, PersonBlockForm},
|
person_block::{PersonBlock, PersonBlockForm},
|
||||||
post::{Post, PostInsertForm, PostUpdateForm},
|
post::{Post, PostInsertForm, PostUpdateForm},
|
||||||
|
@ -374,6 +366,7 @@ mod tests {
|
||||||
inserted_comment_0: Comment,
|
inserted_comment_0: Comment,
|
||||||
inserted_comment_1: Comment,
|
inserted_comment_1: Comment,
|
||||||
inserted_comment_2: Comment,
|
inserted_comment_2: Comment,
|
||||||
|
inserted_comment_5: Comment,
|
||||||
inserted_post: Post,
|
inserted_post: Post,
|
||||||
timmy_local_user_view: LocalUserView,
|
timmy_local_user_view: LocalUserView,
|
||||||
inserted_sara_person: Person,
|
inserted_sara_person: Person,
|
||||||
|
@ -481,7 +474,7 @@ mod tests {
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
"Comment 5".into(),
|
"Comment 5".into(),
|
||||||
);
|
);
|
||||||
let _inserted_comment_5 =
|
let inserted_comment_5 =
|
||||||
Comment::create(pool, &comment_form_5, Some(&inserted_comment_4.path)).await?;
|
Comment::create(pool, &comment_form_5, Some(&inserted_comment_4.path)).await?;
|
||||||
|
|
||||||
let timmy_blocks_sara_form = PersonBlockForm {
|
let timmy_blocks_sara_form = PersonBlockForm {
|
||||||
|
@ -508,9 +501,7 @@ mod tests {
|
||||||
|
|
||||||
let timmy_local_user_view = LocalUserView {
|
let timmy_local_user_view = LocalUserView {
|
||||||
local_user: inserted_timmy_local_user.clone(),
|
local_user: inserted_timmy_local_user.clone(),
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
|
||||||
person: inserted_timmy_person.clone(),
|
person: inserted_timmy_person.clone(),
|
||||||
counts: Default::default(),
|
|
||||||
};
|
};
|
||||||
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
|
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
|
||||||
let site = Site::create(pool, &site_form).await?;
|
let site = Site::create(pool, &site_form).await?;
|
||||||
|
@ -519,6 +510,7 @@ mod tests {
|
||||||
inserted_comment_0,
|
inserted_comment_0,
|
||||||
inserted_comment_1,
|
inserted_comment_1,
|
||||||
inserted_comment_2,
|
inserted_comment_2,
|
||||||
|
inserted_comment_5,
|
||||||
inserted_post,
|
inserted_post,
|
||||||
timmy_local_user_view,
|
timmy_local_user_view,
|
||||||
inserted_sara_person,
|
inserted_sara_person,
|
||||||
|
@ -534,7 +526,7 @@ mod tests {
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
let data = init_data(pool).await?;
|
let data = init_data(pool).await?;
|
||||||
|
|
||||||
let expected_comment_view_no_person = expected_comment_view(&data, pool).await?;
|
let expected_comment_view_no_person = expected_comment_view(&data);
|
||||||
|
|
||||||
let mut expected_comment_view_with_person = expected_comment_view_no_person.clone();
|
let mut expected_comment_view_with_person = expected_comment_view_no_person.clone();
|
||||||
expected_comment_view_with_person.my_vote = Some(1);
|
expected_comment_view_with_person.my_vote = Some(1);
|
||||||
|
@ -666,10 +658,10 @@ mod tests {
|
||||||
// Make sure it contains the parent, but not the comment from the other tree
|
// Make sure it contains the parent, but not the comment from the other tree
|
||||||
let child_comments = read_comment_views_child_path
|
let child_comments = read_comment_views_child_path
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|c| c.comment)
|
.map(|c| c.comment.id)
|
||||||
.collect::<Vec<Comment>>();
|
.collect::<Vec<CommentId>>();
|
||||||
assert!(child_comments.contains(&data.inserted_comment_1));
|
assert!(child_comments.contains(&data.inserted_comment_1.id));
|
||||||
assert!(!child_comments.contains(&data.inserted_comment_2));
|
assert!(!child_comments.contains(&data.inserted_comment_2.id));
|
||||||
|
|
||||||
let read_comment_views_top_max_depth = CommentQuery {
|
let read_comment_views_top_max_depth = CommentQuery {
|
||||||
post_id: (Some(data.inserted_post.id)),
|
post_id: (Some(data.inserted_post.id)),
|
||||||
|
@ -681,7 +673,7 @@ mod tests {
|
||||||
|
|
||||||
// Make sure a depth limited one only has the top comment
|
// Make sure a depth limited one only has the top comment
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_comment_view(&data, pool).await?,
|
expected_comment_view(&data),
|
||||||
read_comment_views_top_max_depth[0]
|
read_comment_views_top_max_depth[0]
|
||||||
);
|
);
|
||||||
assert_length!(1, read_comment_views_top_max_depth);
|
assert_length!(1, read_comment_views_top_max_depth);
|
||||||
|
@ -867,9 +859,8 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn expected_comment_view(data: &Data, pool: &mut DbPool<'_>) -> LemmyResult<CommentView> {
|
fn expected_comment_view(data: &Data) -> CommentView {
|
||||||
let agg = CommentAggregates::read(pool, data.inserted_comment_0.id).await?;
|
CommentView {
|
||||||
Ok(CommentView {
|
|
||||||
creator_banned_from_community: false,
|
creator_banned_from_community: false,
|
||||||
banned_from_community: false,
|
banned_from_community: false,
|
||||||
creator_is_moderator: false,
|
creator_is_moderator: false,
|
||||||
|
@ -893,6 +884,14 @@ mod tests {
|
||||||
distinguished: false,
|
distinguished: false,
|
||||||
path: data.inserted_comment_0.clone().path,
|
path: data.inserted_comment_0.clone().path,
|
||||||
language_id: LanguageId(37),
|
language_id: LanguageId(37),
|
||||||
|
score: 1,
|
||||||
|
upvotes: 1,
|
||||||
|
downvotes: 0,
|
||||||
|
child_count: 5,
|
||||||
|
hot_rank: RANK_DEFAULT,
|
||||||
|
controversy_rank: 0.0,
|
||||||
|
report_count: 0,
|
||||||
|
unresolved_report_count: 0,
|
||||||
},
|
},
|
||||||
creator: Person {
|
creator: Person {
|
||||||
id: data.timmy_local_user_view.person.id,
|
id: data.timmy_local_user_view.person.id,
|
||||||
|
@ -915,6 +914,10 @@ mod tests {
|
||||||
private_key: data.timmy_local_user_view.person.private_key.clone(),
|
private_key: data.timmy_local_user_view.person.private_key.clone(),
|
||||||
public_key: data.timmy_local_user_view.person.public_key.clone(),
|
public_key: data.timmy_local_user_view.person.public_key.clone(),
|
||||||
last_refreshed_at: data.timmy_local_user_view.person.last_refreshed_at,
|
last_refreshed_at: data.timmy_local_user_view.person.last_refreshed_at,
|
||||||
|
post_count: 1,
|
||||||
|
post_score: 0,
|
||||||
|
comment_count: 5,
|
||||||
|
comment_score: 1,
|
||||||
},
|
},
|
||||||
post: Post {
|
post: Post {
|
||||||
id: data.inserted_post.id,
|
id: data.inserted_post.id,
|
||||||
|
@ -941,6 +944,19 @@ mod tests {
|
||||||
featured_local: false,
|
featured_local: false,
|
||||||
url_content_type: None,
|
url_content_type: None,
|
||||||
scheduled_publish_time: None,
|
scheduled_publish_time: None,
|
||||||
|
comments: 6,
|
||||||
|
score: 0,
|
||||||
|
upvotes: 0,
|
||||||
|
downvotes: 0,
|
||||||
|
newest_comment_time_necro: data.inserted_comment_1.published,
|
||||||
|
newest_comment_time: data.inserted_comment_5.published,
|
||||||
|
hot_rank: RANK_DEFAULT,
|
||||||
|
hot_rank_active: RANK_DEFAULT,
|
||||||
|
controversy_rank: 0.0,
|
||||||
|
scaled_rank: RANK_DEFAULT,
|
||||||
|
instance_id: data.inserted_instance.id,
|
||||||
|
report_count: 0,
|
||||||
|
unresolved_report_count: 0,
|
||||||
},
|
},
|
||||||
community: Community {
|
community: Community {
|
||||||
id: data.inserted_community.id,
|
id: data.inserted_community.id,
|
||||||
|
@ -969,20 +985,20 @@ mod tests {
|
||||||
featured_url: data.inserted_community.featured_url.clone(),
|
featured_url: data.inserted_community.featured_url.clone(),
|
||||||
visibility: CommunityVisibility::Public,
|
visibility: CommunityVisibility::Public,
|
||||||
random_number: data.inserted_community.random_number,
|
random_number: data.inserted_community.random_number,
|
||||||
},
|
subscribers: 0,
|
||||||
counts: CommentAggregates {
|
posts: 1,
|
||||||
comment_id: data.inserted_comment_0.id,
|
comments: 6,
|
||||||
score: 1,
|
users_active_day: 0,
|
||||||
upvotes: 1,
|
users_active_week: 0,
|
||||||
downvotes: 0,
|
users_active_month: 0,
|
||||||
published: agg.published,
|
users_active_half_year: 0,
|
||||||
child_count: 5,
|
|
||||||
hot_rank: RANK_DEFAULT,
|
hot_rank: RANK_DEFAULT,
|
||||||
controversy_rank: 0.0,
|
subscribers_local: 0,
|
||||||
report_count: 0,
|
report_count: 0,
|
||||||
unresolved_report_count: 0,
|
unresolved_report_count: 0,
|
||||||
|
interactions_month: 0,
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
|
@ -12,7 +12,7 @@ use diesel_async::RunQueryDsl;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
impls::local_user::LocalUserOptionHelper,
|
impls::local_user::LocalUserOptionHelper,
|
||||||
newtypes::{CommunityId, PersonId},
|
newtypes::{CommunityId, PersonId},
|
||||||
schema::{community, community_actions, community_aggregates, instance_actions, local_user},
|
schema::{community, community_actions, instance_actions, local_user},
|
||||||
source::{
|
source::{
|
||||||
community::{Community, CommunityFollowerState},
|
community::{Community, CommunityFollowerState},
|
||||||
local_user::LocalUser,
|
local_user::LocalUser,
|
||||||
|
@ -41,7 +41,6 @@ impl CommunityView {
|
||||||
let local_user_join = local_user::table.on(local_user::person_id.nullable().eq(person_id));
|
let local_user_join = local_user::table.on(local_user::person_id.nullable().eq(person_id));
|
||||||
|
|
||||||
community::table
|
community::table
|
||||||
.inner_join(community_aggregates::table)
|
|
||||||
.left_join(community_actions_join)
|
.left_join(community_actions_join)
|
||||||
.left_join(instance_actions_join)
|
.left_join(instance_actions_join)
|
||||||
.left_join(local_user_join)
|
.left_join(local_user_join)
|
||||||
|
@ -164,27 +163,24 @@ impl CommunityQuery<'_> {
|
||||||
query = o.local_user.visible_communities_only(query);
|
query = o.local_user.visible_communities_only(query);
|
||||||
|
|
||||||
match o.sort.unwrap_or_default() {
|
match o.sort.unwrap_or_default() {
|
||||||
Hot => query = query.order_by(community_aggregates::hot_rank.desc()),
|
Hot => query = query.order_by(community::hot_rank.desc()),
|
||||||
Comments => query = query.order_by(community_aggregates::comments.desc()),
|
Comments => query = query.order_by(community::comments.desc()),
|
||||||
Posts => query = query.order_by(community_aggregates::posts.desc()),
|
Posts => query = query.order_by(community::posts.desc()),
|
||||||
New => query = query.order_by(community::published.desc()),
|
New => query = query.order_by(community::published.desc()),
|
||||||
Old => query = query.order_by(community::published.asc()),
|
Old => query = query.order_by(community::published.asc()),
|
||||||
Subscribers => query = query.order_by(community_aggregates::subscribers.desc()),
|
Subscribers => query = query.order_by(community::subscribers.desc()),
|
||||||
SubscribersLocal => query = query.order_by(community_aggregates::subscribers_local.desc()),
|
SubscribersLocal => query = query.order_by(community::subscribers_local.desc()),
|
||||||
ActiveSixMonths => {
|
ActiveSixMonths => query = query.order_by(community::users_active_half_year.desc()),
|
||||||
query = query.order_by(community_aggregates::users_active_half_year.desc())
|
ActiveMonthly => query = query.order_by(community::users_active_month.desc()),
|
||||||
}
|
ActiveWeekly => query = query.order_by(community::users_active_week.desc()),
|
||||||
ActiveMonthly => query = query.order_by(community_aggregates::users_active_month.desc()),
|
ActiveDaily => query = query.order_by(community::users_active_day.desc()),
|
||||||
ActiveWeekly => query = query.order_by(community_aggregates::users_active_week.desc()),
|
|
||||||
ActiveDaily => query = query.order_by(community_aggregates::users_active_day.desc()),
|
|
||||||
NameAsc => query = query.order_by(lower(community::name).asc()),
|
NameAsc => query = query.order_by(lower(community::name).asc()),
|
||||||
NameDesc => query = query.order_by(lower(community::name).desc()),
|
NameDesc => query = query.order_by(lower(community::name).desc()),
|
||||||
};
|
};
|
||||||
// Filter by the time range
|
// Filter by the time range
|
||||||
if let Some(time_range_seconds) = o.time_range_seconds {
|
if let Some(time_range_seconds) = o.time_range_seconds {
|
||||||
query = query.filter(
|
query =
|
||||||
community_aggregates::published.gt(now() - seconds_to_pg_interval(time_range_seconds)),
|
query.filter(community::published.gt(now() - seconds_to_pg_interval(time_range_seconds)));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (limit, offset) = limit_and_offset(o.page, o.limit)?;
|
let (limit, offset) = limit_and_offset(o.page, o.limit)?;
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
use crate::structs::LocalUserView;
|
use crate::structs::LocalUserView;
|
||||||
use actix_web::{dev::Payload, FromRequest, HttpMessage, HttpRequest};
|
use actix_web::{dev::Payload, FromRequest, HttpMessage, HttpRequest};
|
||||||
use diesel::{
|
use diesel::{result::Error, BoolExpressionMethods, ExpressionMethods, QueryDsl, SelectableHelper};
|
||||||
result::Error,
|
|
||||||
BoolExpressionMethods,
|
|
||||||
ExpressionMethods,
|
|
||||||
JoinOnDsl,
|
|
||||||
QueryDsl,
|
|
||||||
SelectableHelper,
|
|
||||||
};
|
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::{LocalUserId, OAuthProviderId, PersonId},
|
newtypes::{LocalUserId, OAuthProviderId, PersonId},
|
||||||
schema::{local_user, local_user_vote_display_mode, oauth_account, person, person_aggregates},
|
schema::{local_user, oauth_account, person},
|
||||||
source::{
|
source::{
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
local_user::{LocalUser, LocalUserInsertForm},
|
local_user::{LocalUser, LocalUserInsertForm},
|
||||||
|
@ -30,10 +23,7 @@ use std::future::{ready, Ready};
|
||||||
impl LocalUserView {
|
impl LocalUserView {
|
||||||
#[diesel::dsl::auto_type(no_type_alias)]
|
#[diesel::dsl::auto_type(no_type_alias)]
|
||||||
fn joins() -> _ {
|
fn joins() -> _ {
|
||||||
local_user::table
|
local_user::table.inner_join(person::table)
|
||||||
.inner_join(local_user_vote_display_mode::table)
|
|
||||||
.inner_join(person::table)
|
|
||||||
.inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(pool: &mut DbPool<'_>, local_user_id: LocalUserId) -> Result<Self, Error> {
|
pub async fn read(pool: &mut DbPool<'_>, local_user_id: LocalUserId) -> Result<Self, Error> {
|
||||||
|
|
|
@ -10,16 +10,14 @@ use diesel::{
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::PersonId,
|
newtypes::PersonId,
|
||||||
schema::{local_user, person, person_aggregates},
|
schema::{local_user, person},
|
||||||
utils::{get_conn, now, DbPool},
|
utils::{get_conn, now, DbPool},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl PersonView {
|
impl PersonView {
|
||||||
#[diesel::dsl::auto_type(no_type_alias)]
|
#[diesel::dsl::auto_type(no_type_alias)]
|
||||||
fn joins() -> _ {
|
fn joins() -> _ {
|
||||||
person::table
|
person::table.left_join(local_user::table)
|
||||||
.inner_join(person_aggregates::table)
|
|
||||||
.left_join(local_user::table)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(
|
pub async fn read(
|
||||||
|
|
|
@ -19,7 +19,6 @@ use diesel::{
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::{post_aggregates_keys as key, PostAggregates},
|
|
||||||
aliases::{creator_community_actions, creator_local_user},
|
aliases::{creator_community_actions, creator_local_user},
|
||||||
impls::{
|
impls::{
|
||||||
community::community_follower_select_subscribed_type,
|
community::community_follower_select_subscribed_type,
|
||||||
|
@ -37,16 +36,16 @@ use lemmy_db_schema::{
|
||||||
person_actions,
|
person_actions,
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
|
||||||
post_tag,
|
post_tag,
|
||||||
tag,
|
tag,
|
||||||
},
|
},
|
||||||
source::{
|
source::{
|
||||||
community::CommunityFollowerState,
|
community::CommunityFollowerState,
|
||||||
local_user::LocalUser,
|
local_user::LocalUser,
|
||||||
post::{post_actions_keys, PostActionsCursor},
|
post::{post_actions_keys, post_keys as key, Post, PostActionsCursor},
|
||||||
site::Site,
|
site::Site,
|
||||||
},
|
},
|
||||||
|
traits::Crud,
|
||||||
utils::{
|
utils::{
|
||||||
functions::coalesce,
|
functions::coalesce,
|
||||||
fuzzy_search,
|
fuzzy_search,
|
||||||
|
@ -75,36 +74,36 @@ impl PostView {
|
||||||
fn joins(my_person_id: Option<PersonId>) -> _ {
|
fn joins(my_person_id: Option<PersonId>) -> _ {
|
||||||
let community_actions_join = community_actions::table.on(
|
let community_actions_join = community_actions::table.on(
|
||||||
community_actions::community_id
|
community_actions::community_id
|
||||||
.eq(post_aggregates::community_id)
|
.eq(post::community_id)
|
||||||
.and(community_actions::person_id.nullable().eq(my_person_id)),
|
.and(community_actions::person_id.nullable().eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let person_actions_join = person_actions::table.on(
|
let person_actions_join = person_actions::table.on(
|
||||||
person_actions::target_id
|
person_actions::target_id
|
||||||
.eq(post_aggregates::creator_id)
|
.eq(post::creator_id)
|
||||||
.and(person_actions::person_id.nullable().eq(my_person_id)),
|
.and(person_actions::person_id.nullable().eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let post_actions_join = post_actions::table.on(
|
let post_actions_join = post_actions::table.on(
|
||||||
post_actions::post_id
|
post_actions::post_id
|
||||||
.eq(post_aggregates::post_id)
|
.eq(post::id)
|
||||||
.and(post_actions::person_id.nullable().eq(my_person_id)),
|
.and(post_actions::person_id.nullable().eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let instance_actions_join = instance_actions::table.on(
|
let instance_actions_join = instance_actions::table.on(
|
||||||
instance_actions::instance_id
|
instance_actions::instance_id
|
||||||
.eq(post_aggregates::instance_id)
|
.eq(post::instance_id)
|
||||||
.and(instance_actions::person_id.nullable().eq(my_person_id)),
|
.and(instance_actions::person_id.nullable().eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let post_creator_community_actions_join = creator_community_actions.on(
|
let post_creator_community_actions_join = creator_community_actions.on(
|
||||||
creator_community_actions
|
creator_community_actions
|
||||||
.field(community_actions::community_id)
|
.field(community_actions::community_id)
|
||||||
.eq(post_aggregates::community_id)
|
.eq(post::community_id)
|
||||||
.and(
|
.and(
|
||||||
creator_community_actions
|
creator_community_actions
|
||||||
.field(community_actions::person_id)
|
.field(community_actions::person_id)
|
||||||
.eq(post_aggregates::creator_id),
|
.eq(post::creator_id),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -113,10 +112,9 @@ impl PostView {
|
||||||
|
|
||||||
let local_user_join = local_user::table.on(local_user::person_id.nullable().eq(my_person_id));
|
let local_user_join = local_user::table.on(local_user::person_id.nullable().eq(my_person_id));
|
||||||
|
|
||||||
post_aggregates::table
|
post::table
|
||||||
.inner_join(person::table)
|
.inner_join(person::table)
|
||||||
.inner_join(community::table)
|
.inner_join(community::table)
|
||||||
.inner_join(post::table)
|
|
||||||
.left_join(image_details_join)
|
.left_join(image_details_join)
|
||||||
.left_join(community_actions_join)
|
.left_join(community_actions_join)
|
||||||
.left_join(person_actions_join)
|
.left_join(person_actions_join)
|
||||||
|
@ -130,7 +128,7 @@ impl PostView {
|
||||||
fn creator_is_admin() -> _ {
|
fn creator_is_admin() -> _ {
|
||||||
exists(
|
exists(
|
||||||
creator_local_user.filter(
|
creator_local_user.filter(
|
||||||
post_aggregates::creator_id
|
post::creator_id
|
||||||
.eq(creator_local_user.field(local_user::person_id))
|
.eq(creator_local_user.field(local_user::person_id))
|
||||||
.and(creator_local_user.field(local_user::admin).eq(true)),
|
.and(creator_local_user.field(local_user::admin).eq(true)),
|
||||||
),
|
),
|
||||||
|
@ -151,12 +149,12 @@ impl PostView {
|
||||||
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
|
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
|
||||||
"json_agg(tag.*)",
|
"json_agg(tag.*)",
|
||||||
))
|
))
|
||||||
.filter(post_tag::post_id.eq(post_aggregates::post_id))
|
.filter(post_tag::post_id.eq(post::id))
|
||||||
.filter(tag::deleted.eq(false))
|
.filter(tag::deleted.eq(false))
|
||||||
.single_value();
|
.single_value();
|
||||||
|
|
||||||
let mut query = Self::joins(my_person_id)
|
let mut query = Self::joins(my_person_id)
|
||||||
.filter(post_aggregates::post_id.eq(post_id))
|
.filter(post::id.eq(post_id))
|
||||||
.select((
|
.select((
|
||||||
post::all_columns,
|
post::all_columns,
|
||||||
person::all_columns,
|
person::all_columns,
|
||||||
|
@ -172,7 +170,6 @@ impl PostView {
|
||||||
.nullable()
|
.nullable()
|
||||||
.is_not_null(),
|
.is_not_null(),
|
||||||
Self::creator_is_admin(),
|
Self::creator_is_admin(),
|
||||||
post_aggregates::all_columns,
|
|
||||||
community_follower_select_subscribed_type(),
|
community_follower_select_subscribed_type(),
|
||||||
post_actions::saved.nullable(),
|
post_actions::saved.nullable(),
|
||||||
post_actions::read.nullable().is_not_null(),
|
post_actions::read.nullable().is_not_null(),
|
||||||
|
@ -180,8 +177,8 @@ impl PostView {
|
||||||
person_actions::blocked.nullable().is_not_null(),
|
person_actions::blocked.nullable().is_not_null(),
|
||||||
post_actions::like_score.nullable(),
|
post_actions::like_score.nullable(),
|
||||||
coalesce(
|
coalesce(
|
||||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
post::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
post_aggregates::comments,
|
post::comments,
|
||||||
),
|
),
|
||||||
post_tags,
|
post_tags,
|
||||||
local_user_can_mod(),
|
local_user_can_mod(),
|
||||||
|
@ -234,7 +231,7 @@ impl PostPaginationCursor {
|
||||||
// get cursor for page that starts immediately after the given post
|
// get cursor for page that starts immediately after the given post
|
||||||
pub fn after_post(view: &PostView) -> PostPaginationCursor {
|
pub fn after_post(view: &PostView) -> PostPaginationCursor {
|
||||||
// hex encoding to prevent ossification
|
// hex encoding to prevent ossification
|
||||||
PostPaginationCursor(format!("P{:x}", view.counts.post_id.0))
|
PostPaginationCursor(format!("P{:x}", view.post.id.0))
|
||||||
}
|
}
|
||||||
pub async fn read(
|
pub async fn read(
|
||||||
&self,
|
&self,
|
||||||
|
@ -249,13 +246,10 @@ impl PostPaginationCursor {
|
||||||
.and_then(|e| i32::from_str_radix(e, 16).ok())
|
.and_then(|e| i32::from_str_radix(e, 16).ok())
|
||||||
.ok_or_else(err_msg)?,
|
.ok_or_else(err_msg)?,
|
||||||
);
|
);
|
||||||
let post_aggregates = PostAggregates::read(pool, post_id).await?;
|
let post = Post::read(pool, post_id).await?;
|
||||||
let post_actions = PostActionsCursor::read(pool, post_id, local_user.person_id()).await?;
|
let post_actions = PostActionsCursor::read(pool, post_id, local_user.person_id()).await?;
|
||||||
|
|
||||||
Ok(PaginationCursorData {
|
Ok(PaginationCursorData { post, post_actions })
|
||||||
post_aggregates,
|
|
||||||
post_actions,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +257,7 @@ impl PostPaginationCursor {
|
||||||
// we only use some of the properties, depending on which sort type we page by
|
// we only use some of the properties, depending on which sort type we page by
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PaginationCursorData {
|
pub struct PaginationCursorData {
|
||||||
post_aggregates: PostAggregates,
|
post: Post,
|
||||||
post_actions: PostActionsCursor,
|
post_actions: PostActionsCursor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +283,7 @@ pub struct PostQuery<'a> {
|
||||||
pub limit: Option<i64>,
|
pub limit: Option<i64>,
|
||||||
// TODO these should be simple cursors like the others, not data
|
// TODO these should be simple cursors like the others, not data
|
||||||
pub page_after: Option<PaginationCursorData>,
|
pub page_after: Option<PaginationCursorData>,
|
||||||
pub page_before_or_equal: Option<PostAggregates>,
|
pub page_before_or_equal: Option<Post>,
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
pub show_hidden: Option<bool>,
|
pub show_hidden: Option<bool>,
|
||||||
pub show_read: Option<bool>,
|
pub show_read: Option<bool>,
|
||||||
|
@ -317,11 +311,6 @@ impl<'a> PostQuery<'a> {
|
||||||
// covers the "worst case" of the whole page consisting of posts from one community
|
// covers the "worst case" of the whole page consisting of posts from one community
|
||||||
// but using the largest community decreases the pagination-frame so make the real query more
|
// but using the largest community decreases the pagination-frame so make the real query more
|
||||||
// efficient.
|
// efficient.
|
||||||
use lemmy_db_schema::schema::community_aggregates::dsl::{
|
|
||||||
community_aggregates,
|
|
||||||
community_id,
|
|
||||||
users_active_month,
|
|
||||||
};
|
|
||||||
let (limit, offset) = limit_and_offset(self.page, self.limit)?;
|
let (limit, offset) = limit_and_offset(self.page, self.limit)?;
|
||||||
if offset != 0 && self.page_after.is_some() {
|
if offset != 0 && self.page_after.is_some() {
|
||||||
return Err(Error::QueryBuilderError(
|
return Err(Error::QueryBuilderError(
|
||||||
|
@ -334,9 +323,9 @@ impl<'a> PostQuery<'a> {
|
||||||
community_actions::table
|
community_actions::table
|
||||||
.filter(community_actions::followed.is_not_null())
|
.filter(community_actions::followed.is_not_null())
|
||||||
.filter(community_actions::person_id.eq(self_person_id))
|
.filter(community_actions::person_id.eq(self_person_id))
|
||||||
.inner_join(community_aggregates.on(community_id.eq(community_actions::community_id)))
|
.inner_join(community::table.on(community::id.eq(community_actions::community_id)))
|
||||||
.order_by(users_active_month.desc())
|
.order_by(community::users_active_month.desc())
|
||||||
.select(community_id)
|
.select(community::id)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.get_result::<CommunityId>(conn)
|
.get_result::<CommunityId>(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -369,7 +358,7 @@ impl<'a> PostQuery<'a> {
|
||||||
} else {
|
} else {
|
||||||
v.pop()
|
v.pop()
|
||||||
};
|
};
|
||||||
let limit_cursor = Some(item.expect("else case").counts);
|
let limit_cursor = Some(item.expect("else case").post);
|
||||||
Ok(Some(PostQuery {
|
Ok(Some(PostQuery {
|
||||||
page_before_or_equal: limit_cursor,
|
page_before_or_equal: limit_cursor,
|
||||||
..self.clone()
|
..self.clone()
|
||||||
|
@ -405,7 +394,7 @@ impl<'a> PostQuery<'a> {
|
||||||
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
|
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
|
||||||
"json_agg(tag.*)",
|
"json_agg(tag.*)",
|
||||||
))
|
))
|
||||||
.filter(post_tag::post_id.eq(post_aggregates::post_id))
|
.filter(post_tag::post_id.eq(post::id))
|
||||||
.filter(tag::deleted.eq(false))
|
.filter(tag::deleted.eq(false))
|
||||||
.single_value();
|
.single_value();
|
||||||
|
|
||||||
|
@ -425,7 +414,6 @@ impl<'a> PostQuery<'a> {
|
||||||
.nullable()
|
.nullable()
|
||||||
.is_not_null(),
|
.is_not_null(),
|
||||||
PostView::creator_is_admin(),
|
PostView::creator_is_admin(),
|
||||||
post_aggregates::all_columns,
|
|
||||||
community_follower_select_subscribed_type(),
|
community_follower_select_subscribed_type(),
|
||||||
post_actions::saved.nullable(),
|
post_actions::saved.nullable(),
|
||||||
post_actions::read.nullable().is_not_null(),
|
post_actions::read.nullable().is_not_null(),
|
||||||
|
@ -433,8 +421,8 @@ impl<'a> PostQuery<'a> {
|
||||||
person_actions::blocked.nullable().is_not_null(),
|
person_actions::blocked.nullable().is_not_null(),
|
||||||
post_actions::like_score.nullable(),
|
post_actions::like_score.nullable(),
|
||||||
coalesce(
|
coalesce(
|
||||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
post::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
post_aggregates::comments,
|
post::comments,
|
||||||
),
|
),
|
||||||
post_tags,
|
post_tags,
|
||||||
local_user_can_mod(),
|
local_user_can_mod(),
|
||||||
|
@ -465,11 +453,11 @@ impl<'a> PostQuery<'a> {
|
||||||
.filter(post::removed.eq(false));
|
.filter(post::removed.eq(false));
|
||||||
}
|
}
|
||||||
if let Some(community_id) = o.community_id {
|
if let Some(community_id) = o.community_id {
|
||||||
query = query.filter(post_aggregates::community_id.eq(community_id));
|
query = query.filter(post::community_id.eq(community_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(creator_id) = o.creator_id {
|
if let Some(creator_id) = o.creator_id {
|
||||||
query = query.filter(post_aggregates::creator_id.eq(creator_id));
|
query = query.filter(post::creator_id.eq(creator_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_subscribed = community_actions::followed.is_not_null();
|
let is_subscribed = community_actions::followed.is_not_null();
|
||||||
|
@ -521,7 +509,7 @@ impl<'a> PostQuery<'a> {
|
||||||
|
|
||||||
// Filter to show only posts with no comments
|
// Filter to show only posts with no comments
|
||||||
if o.no_comments_only.unwrap_or_default() {
|
if o.no_comments_only.unwrap_or_default() {
|
||||||
query = query.filter(post_aggregates::comments.eq(0));
|
query = query.filter(post::comments.eq(0));
|
||||||
};
|
};
|
||||||
|
|
||||||
if !o.show_read.unwrap_or(o.local_user.show_read_posts()) {
|
if !o.show_read.unwrap_or(o.local_user.show_read_posts()) {
|
||||||
|
@ -548,7 +536,7 @@ impl<'a> PostQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(my_id) = o.local_user.person_id() {
|
if let Some(my_id) = o.local_user.person_id() {
|
||||||
let not_creator_filter = post_aggregates::creator_id.ne(my_id);
|
let not_creator_filter = post::creator_id.ne(my_id);
|
||||||
if o.liked_only.unwrap_or_default() {
|
if o.liked_only.unwrap_or_default() {
|
||||||
query = query
|
query = query
|
||||||
.filter(not_creator_filter)
|
.filter(not_creator_filter)
|
||||||
|
@ -604,7 +592,7 @@ impl<'a> PostQuery<'a> {
|
||||||
} else {
|
} else {
|
||||||
let mut query = paginate(
|
let mut query = paginate(
|
||||||
query,
|
query,
|
||||||
o.page_after.map(|c| c.post_aggregates),
|
o.page_after.map(|c| c.post),
|
||||||
o.page_before_or_equal,
|
o.page_before_or_equal,
|
||||||
o.page_back.unwrap_or_default(),
|
o.page_back.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
@ -631,9 +619,8 @@ impl<'a> PostQuery<'a> {
|
||||||
|
|
||||||
// Filter by the time range
|
// Filter by the time range
|
||||||
if let Some(time_range_seconds) = o.time_range_seconds {
|
if let Some(time_range_seconds) = o.time_range_seconds {
|
||||||
query = query.filter(
|
query =
|
||||||
post_aggregates::published.gt(now() - seconds_to_pg_interval(time_range_seconds)),
|
query.filter(post::published.gt(now() - seconds_to_pg_interval(time_range_seconds)));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// use publish as fallback. especially useful for hot rank which reaches zero after some days.
|
// use publish as fallback. especially useful for hot rank which reaches zero after some days.
|
||||||
|
@ -645,7 +632,7 @@ impl<'a> PostQuery<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// finally use unique post id as tie breaker
|
// finally use unique post id as tie breaker
|
||||||
query = query.then_desc(key::post_id);
|
query = query.then_desc(key::id);
|
||||||
|
|
||||||
query.as_query()
|
query.as_query()
|
||||||
};
|
};
|
||||||
|
@ -674,7 +661,6 @@ mod tests {
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use diesel_async::SimpleAsyncConnection;
|
use diesel_async::SimpleAsyncConnection;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::PostAggregates,
|
|
||||||
impls::actor_language::UNDETERMINED_ID,
|
impls::actor_language::UNDETERMINED_ID,
|
||||||
newtypes::LanguageId,
|
newtypes::LanguageId,
|
||||||
source::{
|
source::{
|
||||||
|
@ -697,7 +683,6 @@ mod tests {
|
||||||
instance_block::{InstanceBlock, InstanceBlockForm},
|
instance_block::{InstanceBlock, InstanceBlockForm},
|
||||||
language::Language,
|
language::Language,
|
||||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
person_block::{PersonBlock, PersonBlockForm},
|
person_block::{PersonBlock, PersonBlockForm},
|
||||||
post::{
|
post::{
|
||||||
|
@ -894,22 +879,16 @@ mod tests {
|
||||||
|
|
||||||
let tegan_local_user_view = LocalUserView {
|
let tegan_local_user_view = LocalUserView {
|
||||||
local_user: inserted_tegan_local_user,
|
local_user: inserted_tegan_local_user,
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
|
||||||
person: inserted_tegan_person,
|
person: inserted_tegan_person,
|
||||||
counts: Default::default(),
|
|
||||||
};
|
};
|
||||||
let john_local_user_view = LocalUserView {
|
let john_local_user_view = LocalUserView {
|
||||||
local_user: inserted_john_local_user,
|
local_user: inserted_john_local_user,
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
|
||||||
person: inserted_john_person,
|
person: inserted_john_person,
|
||||||
counts: Default::default(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let bot_local_user_view = LocalUserView {
|
let bot_local_user_view = LocalUserView {
|
||||||
local_user: inserted_bot_local_user,
|
local_user: inserted_bot_local_user,
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
|
||||||
person: inserted_bot_person,
|
person: inserted_bot_person,
|
||||||
counts: Default::default(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let site = Site {
|
let site = Site {
|
||||||
|
@ -1004,7 +983,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let expected_post_listing_with_user = expected_post_view(data, pool).await?;
|
let expected_post_listing_with_user = expected_post_view(data)?;
|
||||||
|
|
||||||
// Should be only one person, IE the bot post, and blocked should be missing
|
// Should be only one person, IE the bot post, and blocked should be missing
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1060,7 +1039,7 @@ mod tests {
|
||||||
let read_post_listing_single_no_person =
|
let read_post_listing_single_no_person =
|
||||||
PostView::read(pool, data.post.id, None, false).await?;
|
PostView::read(pool, data.post.id, None, false).await?;
|
||||||
|
|
||||||
let mut expected_post_listing_no_person = expected_post_view(data, pool).await?;
|
let mut expected_post_listing_no_person = expected_post_view(data)?;
|
||||||
expected_post_listing_no_person.can_mod = false;
|
expected_post_listing_no_person.can_mod = false;
|
||||||
|
|
||||||
// Should be 2 posts, with the bot post, and the blocked
|
// Should be 2 posts, with the bot post, and the blocked
|
||||||
|
@ -1193,10 +1172,11 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut expected_post_with_upvote = expected_post_view(data, pool).await?;
|
let mut expected_post_with_upvote = expected_post_view(data)?;
|
||||||
expected_post_with_upvote.my_vote = Some(1);
|
expected_post_with_upvote.my_vote = Some(1);
|
||||||
expected_post_with_upvote.counts.score = 1;
|
expected_post_with_upvote.post.score = 1;
|
||||||
expected_post_with_upvote.counts.upvotes = 1;
|
expected_post_with_upvote.post.upvotes = 1;
|
||||||
|
expected_post_with_upvote.creator.post_score = 1;
|
||||||
assert_eq!(expected_post_with_upvote, post_listing_single_with_person);
|
assert_eq!(expected_post_with_upvote, post_listing_single_with_person);
|
||||||
|
|
||||||
let local_user_form = LocalUserUpdateForm {
|
let local_user_form = LocalUserUpdateForm {
|
||||||
|
@ -1745,7 +1725,7 @@ mod tests {
|
||||||
loop {
|
loop {
|
||||||
let post_listings = PostQuery {
|
let post_listings = PostQuery {
|
||||||
page_after: page_after.map(|p| PaginationCursorData {
|
page_after: page_after.map(|p| PaginationCursorData {
|
||||||
post_aggregates: p,
|
post: p,
|
||||||
post_actions: Default::default(),
|
post_actions: Default::default(),
|
||||||
}),
|
}),
|
||||||
..options.clone()
|
..options.clone()
|
||||||
|
@ -1756,7 +1736,7 @@ mod tests {
|
||||||
listed_post_ids.extend(post_listings.iter().map(|p| p.post.id));
|
listed_post_ids.extend(post_listings.iter().map(|p| p.post.id));
|
||||||
|
|
||||||
if let Some(p) = post_listings.into_iter().next_back() {
|
if let Some(p) = post_listings.into_iter().next_back() {
|
||||||
page_after = Some(p.counts);
|
page_after = Some(p.post);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1768,7 +1748,7 @@ mod tests {
|
||||||
loop {
|
loop {
|
||||||
let post_listings = PostQuery {
|
let post_listings = PostQuery {
|
||||||
page_after: page_before.map(|p| PaginationCursorData {
|
page_after: page_before.map(|p| PaginationCursorData {
|
||||||
post_aggregates: p,
|
post: p,
|
||||||
post_actions: Default::default(),
|
post_actions: Default::default(),
|
||||||
}),
|
}),
|
||||||
page_back: Some(true),
|
page_back: Some(true),
|
||||||
|
@ -1787,7 +1767,7 @@ mod tests {
|
||||||
listed_post_ids_forward.truncate(index);
|
listed_post_ids_forward.truncate(index);
|
||||||
|
|
||||||
if let Some(p) = post_listings.into_iter().next() {
|
if let Some(p) = post_listings.into_iter().next() {
|
||||||
page_before = Some(p.counts);
|
page_before = Some(p.post);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1938,13 +1918,12 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn expected_post_view(data: &Data, pool: &mut DbPool<'_>) -> LemmyResult<PostView> {
|
fn expected_post_view(data: &Data) -> LemmyResult<PostView> {
|
||||||
let (inserted_person, inserted_community, inserted_post) = (
|
let (inserted_person, inserted_community, inserted_post) = (
|
||||||
&data.tegan_local_user_view.person,
|
&data.tegan_local_user_view.person,
|
||||||
&data.community,
|
&data.community,
|
||||||
&data.post,
|
&data.post,
|
||||||
);
|
);
|
||||||
let agg = PostAggregates::read(pool, inserted_post.id).await?;
|
|
||||||
|
|
||||||
Ok(PostView {
|
Ok(PostView {
|
||||||
post: Post {
|
post: Post {
|
||||||
|
@ -1972,6 +1951,19 @@ mod tests {
|
||||||
featured_local: false,
|
featured_local: false,
|
||||||
url_content_type: None,
|
url_content_type: None,
|
||||||
scheduled_publish_time: None,
|
scheduled_publish_time: None,
|
||||||
|
comments: 0,
|
||||||
|
score: 0,
|
||||||
|
upvotes: 0,
|
||||||
|
downvotes: 0,
|
||||||
|
newest_comment_time_necro: inserted_post.published,
|
||||||
|
newest_comment_time: inserted_post.published,
|
||||||
|
hot_rank: RANK_DEFAULT,
|
||||||
|
hot_rank_active: RANK_DEFAULT,
|
||||||
|
controversy_rank: 0.0,
|
||||||
|
scaled_rank: RANK_DEFAULT,
|
||||||
|
instance_id: data.instance.id,
|
||||||
|
report_count: 0,
|
||||||
|
unresolved_report_count: 0,
|
||||||
},
|
},
|
||||||
my_vote: None,
|
my_vote: None,
|
||||||
unread_comments: 0,
|
unread_comments: 0,
|
||||||
|
@ -1996,6 +1988,10 @@ mod tests {
|
||||||
private_key: inserted_person.private_key.clone(),
|
private_key: inserted_person.private_key.clone(),
|
||||||
public_key: inserted_person.public_key.clone(),
|
public_key: inserted_person.public_key.clone(),
|
||||||
last_refreshed_at: inserted_person.last_refreshed_at,
|
last_refreshed_at: inserted_person.last_refreshed_at,
|
||||||
|
post_count: 2,
|
||||||
|
post_score: 0,
|
||||||
|
comment_count: 0,
|
||||||
|
comment_score: 0,
|
||||||
},
|
},
|
||||||
image_details: None,
|
image_details: None,
|
||||||
creator_banned_from_community: false,
|
creator_banned_from_community: false,
|
||||||
|
@ -2030,27 +2026,18 @@ mod tests {
|
||||||
featured_url: inserted_community.featured_url.clone(),
|
featured_url: inserted_community.featured_url.clone(),
|
||||||
visibility: CommunityVisibility::Public,
|
visibility: CommunityVisibility::Public,
|
||||||
random_number: inserted_community.random_number,
|
random_number: inserted_community.random_number,
|
||||||
},
|
subscribers: 0,
|
||||||
counts: PostAggregates {
|
posts: 4,
|
||||||
post_id: inserted_post.id,
|
|
||||||
comments: 0,
|
comments: 0,
|
||||||
score: 0,
|
users_active_day: 0,
|
||||||
upvotes: 0,
|
users_active_week: 0,
|
||||||
downvotes: 0,
|
users_active_month: 0,
|
||||||
published: agg.published,
|
users_active_half_year: 0,
|
||||||
newest_comment_time_necro: inserted_post.published,
|
|
||||||
newest_comment_time: inserted_post.published,
|
|
||||||
featured_community: false,
|
|
||||||
featured_local: false,
|
|
||||||
hot_rank: RANK_DEFAULT,
|
hot_rank: RANK_DEFAULT,
|
||||||
hot_rank_active: RANK_DEFAULT,
|
subscribers_local: 0,
|
||||||
controversy_rank: 0.0,
|
|
||||||
scaled_rank: RANK_DEFAULT,
|
|
||||||
community_id: inserted_post.community_id,
|
|
||||||
creator_id: inserted_post.creator_id,
|
|
||||||
instance_id: data.instance.id,
|
|
||||||
report_count: 0,
|
report_count: 0,
|
||||||
unresolved_report_count: 0,
|
unresolved_report_count: 0,
|
||||||
|
interactions_month: 0,
|
||||||
},
|
},
|
||||||
subscribed: SubscribedType::NotSubscribed,
|
subscribed: SubscribedType::NotSubscribed,
|
||||||
read: false,
|
read: false,
|
||||||
|
|
|
@ -140,28 +140,28 @@ mod tests {
|
||||||
let pool = &build_db_pool_for_tests();
|
let pool = &build_db_pool_for_tests();
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
let instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
let timmy_person_form = PersonInsertForm::test_form(inserted_instance.id, "timmy_rav");
|
let timmy_person_form = PersonInsertForm::test_form(instance.id, "timmy_rav");
|
||||||
|
|
||||||
let inserted_timmy_person = Person::create(pool, &timmy_person_form).await?;
|
let timmy_person = Person::create(pool, &timmy_person_form).await?;
|
||||||
|
|
||||||
let timmy_local_user_form = LocalUserInsertForm::test_form_admin(inserted_timmy_person.id);
|
let timmy_local_user_form = LocalUserInsertForm::test_form_admin(timmy_person.id);
|
||||||
|
|
||||||
let _inserted_timmy_local_user =
|
let _inserted_timmy_local_user =
|
||||||
LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
||||||
|
|
||||||
let sara_person_form = PersonInsertForm::test_form(inserted_instance.id, "sara_rav");
|
let sara_person_form = PersonInsertForm::test_form(instance.id, "sara_rav");
|
||||||
|
|
||||||
let inserted_sara_person = Person::create(pool, &sara_person_form).await?;
|
let sara_person = Person::create(pool, &sara_person_form).await?;
|
||||||
|
|
||||||
let sara_local_user_form = LocalUserInsertForm::test_form(inserted_sara_person.id);
|
let sara_local_user_form = LocalUserInsertForm::test_form(sara_person.id);
|
||||||
|
|
||||||
let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form, vec![]).await?;
|
let sara_local_user = LocalUser::create(pool, &sara_local_user_form, vec![]).await?;
|
||||||
|
|
||||||
// Sara creates an application
|
// Sara creates an application
|
||||||
let sara_app_form = RegistrationApplicationInsertForm {
|
let sara_app_form = RegistrationApplicationInsertForm {
|
||||||
local_user_id: inserted_sara_local_user.id,
|
local_user_id: sara_local_user.id,
|
||||||
answer: "LET ME IIIIINN".to_string(),
|
answer: "LET ME IIIIINN".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,17 +169,17 @@ mod tests {
|
||||||
|
|
||||||
let read_sara_app_view = RegistrationApplicationView::read(pool, sara_app.id).await?;
|
let read_sara_app_view = RegistrationApplicationView::read(pool, sara_app.id).await?;
|
||||||
|
|
||||||
let jess_person_form = PersonInsertForm::test_form(inserted_instance.id, "jess_rav");
|
let jess_person_form = PersonInsertForm::test_form(instance.id, "jess_rav");
|
||||||
|
|
||||||
let inserted_jess_person = Person::create(pool, &jess_person_form).await?;
|
let inserted_jess_person = Person::create(pool, &jess_person_form).await?;
|
||||||
|
|
||||||
let jess_local_user_form = LocalUserInsertForm::test_form(inserted_jess_person.id);
|
let jess_local_user_form = LocalUserInsertForm::test_form(inserted_jess_person.id);
|
||||||
|
|
||||||
let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form, vec![]).await?;
|
let jess_local_user = LocalUser::create(pool, &jess_local_user_form, vec![]).await?;
|
||||||
|
|
||||||
// Sara creates an application
|
// Sara creates an application
|
||||||
let jess_app_form = RegistrationApplicationInsertForm {
|
let jess_app_form = RegistrationApplicationInsertForm {
|
||||||
local_user_id: inserted_jess_local_user.id,
|
local_user_id: jess_local_user.id,
|
||||||
answer: "LET ME IIIIINN".to_string(),
|
answer: "LET ME IIIIINN".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -190,42 +190,44 @@ mod tests {
|
||||||
let mut expected_sara_app_view = RegistrationApplicationView {
|
let mut expected_sara_app_view = RegistrationApplicationView {
|
||||||
registration_application: sara_app.clone(),
|
registration_application: sara_app.clone(),
|
||||||
creator_local_user: LocalUser {
|
creator_local_user: LocalUser {
|
||||||
id: inserted_sara_local_user.id,
|
id: sara_local_user.id,
|
||||||
person_id: inserted_sara_local_user.person_id,
|
person_id: sara_local_user.person_id,
|
||||||
email: inserted_sara_local_user.email,
|
email: sara_local_user.email,
|
||||||
show_nsfw: inserted_sara_local_user.show_nsfw,
|
show_nsfw: sara_local_user.show_nsfw,
|
||||||
blur_nsfw: inserted_sara_local_user.blur_nsfw,
|
blur_nsfw: sara_local_user.blur_nsfw,
|
||||||
theme: inserted_sara_local_user.theme,
|
theme: sara_local_user.theme,
|
||||||
default_post_sort_type: inserted_sara_local_user.default_post_sort_type,
|
default_post_sort_type: sara_local_user.default_post_sort_type,
|
||||||
default_comment_sort_type: inserted_sara_local_user.default_comment_sort_type,
|
default_comment_sort_type: sara_local_user.default_comment_sort_type,
|
||||||
default_listing_type: inserted_sara_local_user.default_listing_type,
|
default_listing_type: sara_local_user.default_listing_type,
|
||||||
interface_language: inserted_sara_local_user.interface_language,
|
interface_language: sara_local_user.interface_language,
|
||||||
show_avatars: inserted_sara_local_user.show_avatars,
|
show_avatars: sara_local_user.show_avatars,
|
||||||
send_notifications_to_email: inserted_sara_local_user.send_notifications_to_email,
|
send_notifications_to_email: sara_local_user.send_notifications_to_email,
|
||||||
show_bot_accounts: inserted_sara_local_user.show_bot_accounts,
|
show_bot_accounts: sara_local_user.show_bot_accounts,
|
||||||
show_read_posts: inserted_sara_local_user.show_read_posts,
|
show_read_posts: sara_local_user.show_read_posts,
|
||||||
email_verified: inserted_sara_local_user.email_verified,
|
email_verified: sara_local_user.email_verified,
|
||||||
accepted_application: inserted_sara_local_user.accepted_application,
|
accepted_application: sara_local_user.accepted_application,
|
||||||
totp_2fa_secret: inserted_sara_local_user.totp_2fa_secret,
|
totp_2fa_secret: sara_local_user.totp_2fa_secret,
|
||||||
password_encrypted: inserted_sara_local_user.password_encrypted,
|
password_encrypted: sara_local_user.password_encrypted,
|
||||||
open_links_in_new_tab: inserted_sara_local_user.open_links_in_new_tab,
|
open_links_in_new_tab: sara_local_user.open_links_in_new_tab,
|
||||||
infinite_scroll_enabled: inserted_sara_local_user.infinite_scroll_enabled,
|
infinite_scroll_enabled: sara_local_user.infinite_scroll_enabled,
|
||||||
post_listing_mode: inserted_sara_local_user.post_listing_mode,
|
post_listing_mode: sara_local_user.post_listing_mode,
|
||||||
totp_2fa_enabled: inserted_sara_local_user.totp_2fa_enabled,
|
totp_2fa_enabled: sara_local_user.totp_2fa_enabled,
|
||||||
enable_keyboard_navigation: inserted_sara_local_user.enable_keyboard_navigation,
|
enable_keyboard_navigation: sara_local_user.enable_keyboard_navigation,
|
||||||
enable_animated_images: inserted_sara_local_user.enable_animated_images,
|
enable_animated_images: sara_local_user.enable_animated_images,
|
||||||
enable_private_messages: inserted_sara_local_user.enable_private_messages,
|
enable_private_messages: sara_local_user.enable_private_messages,
|
||||||
collapse_bot_comments: inserted_sara_local_user.collapse_bot_comments,
|
collapse_bot_comments: sara_local_user.collapse_bot_comments,
|
||||||
last_donation_notification: inserted_sara_local_user.last_donation_notification,
|
last_donation_notification: sara_local_user.last_donation_notification,
|
||||||
|
show_upvotes: sara_local_user.show_upvotes,
|
||||||
|
show_downvotes: sara_local_user.show_downvotes,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
creator: Person {
|
creator: Person {
|
||||||
id: inserted_sara_person.id,
|
id: sara_person.id,
|
||||||
name: inserted_sara_person.name.clone(),
|
name: sara_person.name.clone(),
|
||||||
display_name: None,
|
display_name: None,
|
||||||
published: inserted_sara_person.published,
|
published: sara_person.published,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
ap_id: inserted_sara_person.ap_id.clone(),
|
ap_id: sara_person.ap_id.clone(),
|
||||||
local: true,
|
local: true,
|
||||||
banned: false,
|
banned: false,
|
||||||
ban_expires: None,
|
ban_expires: None,
|
||||||
|
@ -234,12 +236,16 @@ mod tests {
|
||||||
bio: None,
|
bio: None,
|
||||||
banner: None,
|
banner: None,
|
||||||
updated: None,
|
updated: None,
|
||||||
inbox_url: inserted_sara_person.inbox_url.clone(),
|
inbox_url: sara_person.inbox_url.clone(),
|
||||||
matrix_user_id: None,
|
matrix_user_id: None,
|
||||||
instance_id: inserted_instance.id,
|
instance_id: instance.id,
|
||||||
private_key: inserted_sara_person.private_key,
|
private_key: sara_person.private_key,
|
||||||
public_key: inserted_sara_person.public_key,
|
public_key: sara_person.public_key,
|
||||||
last_refreshed_at: inserted_sara_person.last_refreshed_at,
|
last_refreshed_at: sara_person.last_refreshed_at,
|
||||||
|
post_count: 0,
|
||||||
|
post_score: 0,
|
||||||
|
comment_count: 0,
|
||||||
|
comment_score: 0,
|
||||||
},
|
},
|
||||||
admin: None,
|
admin: None,
|
||||||
};
|
};
|
||||||
|
@ -265,7 +271,7 @@ mod tests {
|
||||||
|
|
||||||
// Approve the application
|
// Approve the application
|
||||||
let approve_form = RegistrationApplicationUpdateForm {
|
let approve_form = RegistrationApplicationUpdateForm {
|
||||||
admin_id: Some(Some(inserted_timmy_person.id)),
|
admin_id: Some(Some(timmy_person.id)),
|
||||||
deny_reason: None,
|
deny_reason: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -277,7 +283,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
LocalUser::update(pool, inserted_sara_local_user.id, &approve_local_user_form).await?;
|
LocalUser::update(pool, sara_local_user.id, &approve_local_user_form).await?;
|
||||||
|
|
||||||
let read_sara_app_view_after_approve =
|
let read_sara_app_view_after_approve =
|
||||||
RegistrationApplicationView::read(pool, sara_app.id).await?;
|
RegistrationApplicationView::read(pool, sara_app.id).await?;
|
||||||
|
@ -286,15 +292,15 @@ mod tests {
|
||||||
expected_sara_app_view
|
expected_sara_app_view
|
||||||
.creator_local_user
|
.creator_local_user
|
||||||
.accepted_application = true;
|
.accepted_application = true;
|
||||||
expected_sara_app_view.registration_application.admin_id = Some(inserted_timmy_person.id);
|
expected_sara_app_view.registration_application.admin_id = Some(timmy_person.id);
|
||||||
|
|
||||||
expected_sara_app_view.admin = Some(Person {
|
expected_sara_app_view.admin = Some(Person {
|
||||||
id: inserted_timmy_person.id,
|
id: timmy_person.id,
|
||||||
name: inserted_timmy_person.name.clone(),
|
name: timmy_person.name.clone(),
|
||||||
display_name: None,
|
display_name: None,
|
||||||
published: inserted_timmy_person.published,
|
published: timmy_person.published,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
ap_id: inserted_timmy_person.ap_id.clone(),
|
ap_id: timmy_person.ap_id.clone(),
|
||||||
local: true,
|
local: true,
|
||||||
banned: false,
|
banned: false,
|
||||||
ban_expires: None,
|
ban_expires: None,
|
||||||
|
@ -303,12 +309,16 @@ mod tests {
|
||||||
bio: None,
|
bio: None,
|
||||||
banner: None,
|
banner: None,
|
||||||
updated: None,
|
updated: None,
|
||||||
inbox_url: inserted_timmy_person.inbox_url.clone(),
|
inbox_url: timmy_person.inbox_url.clone(),
|
||||||
matrix_user_id: None,
|
matrix_user_id: None,
|
||||||
instance_id: inserted_instance.id,
|
instance_id: instance.id,
|
||||||
private_key: inserted_timmy_person.private_key,
|
private_key: timmy_person.private_key,
|
||||||
public_key: inserted_timmy_person.public_key,
|
public_key: timmy_person.public_key,
|
||||||
last_refreshed_at: inserted_timmy_person.last_refreshed_at,
|
last_refreshed_at: timmy_person.last_refreshed_at,
|
||||||
|
post_count: 0,
|
||||||
|
post_score: 0,
|
||||||
|
comment_count: 0,
|
||||||
|
comment_score: 0,
|
||||||
});
|
});
|
||||||
assert_eq!(read_sara_app_view_after_approve, expected_sara_app_view);
|
assert_eq!(read_sara_app_view_after_approve, expected_sara_app_view);
|
||||||
|
|
||||||
|
@ -331,10 +341,10 @@ mod tests {
|
||||||
let all_apps = RegistrationApplicationQuery::default().list(pool).await?;
|
let all_apps = RegistrationApplicationQuery::default().list(pool).await?;
|
||||||
assert_eq!(all_apps.len(), 2);
|
assert_eq!(all_apps.len(), 2);
|
||||||
|
|
||||||
Person::delete(pool, inserted_timmy_person.id).await?;
|
Person::delete(pool, timmy_person.id).await?;
|
||||||
Person::delete(pool, inserted_sara_person.id).await?;
|
Person::delete(pool, sara_person.id).await?;
|
||||||
Person::delete(pool, inserted_jess_person.id).await?;
|
Person::delete(pool, inserted_jess_person.id).await?;
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
Instance::delete(pool, instance.id).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ use lemmy_db_schema::{
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
comment_aggregates,
|
|
||||||
comment_report,
|
comment_report,
|
||||||
community,
|
community,
|
||||||
community_actions,
|
community_actions,
|
||||||
|
@ -48,9 +47,6 @@ impl CommentReportView {
|
||||||
|
|
||||||
let comment_creator_join = aliases::person1.on(comment::creator_id.eq(recipient_id));
|
let comment_creator_join = aliases::person1.on(comment::creator_id.eq(recipient_id));
|
||||||
|
|
||||||
let comment_aggregates_join =
|
|
||||||
comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id));
|
|
||||||
|
|
||||||
let comment_actions_join = comment_actions::table.on(
|
let comment_actions_join = comment_actions::table.on(
|
||||||
comment_actions::comment_id
|
comment_actions::comment_id
|
||||||
.eq(comment_report::comment_id)
|
.eq(comment_report::comment_id)
|
||||||
|
@ -88,7 +84,6 @@ impl CommentReportView {
|
||||||
.inner_join(community_join)
|
.inner_join(community_join)
|
||||||
.inner_join(report_creator_join)
|
.inner_join(report_creator_join)
|
||||||
.inner_join(comment_creator_join)
|
.inner_join(comment_creator_join)
|
||||||
.inner_join(comment_aggregates_join)
|
|
||||||
.left_join(comment_actions_join)
|
.left_join(comment_actions_join)
|
||||||
.left_join(resolver_join)
|
.left_join(resolver_join)
|
||||||
.left_join(creator_community_actions_join)
|
.left_join(creator_community_actions_join)
|
||||||
|
@ -115,7 +110,6 @@ impl CommentReportView {
|
||||||
community::all_columns,
|
community::all_columns,
|
||||||
person::all_columns,
|
person::all_columns,
|
||||||
aliases::person1.fields(person::all_columns),
|
aliases::person1.fields(person::all_columns),
|
||||||
comment_aggregates::all_columns,
|
|
||||||
coalesce(
|
coalesce(
|
||||||
creator_community_actions
|
creator_community_actions
|
||||||
.field(community_actions::received_ban)
|
.field(community_actions::received_ban)
|
||||||
|
|
|
@ -20,7 +20,6 @@ use lemmy_db_schema::{
|
||||||
person_actions,
|
person_actions,
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
|
||||||
post_report,
|
post_report,
|
||||||
},
|
},
|
||||||
utils::{functions::coalesce, get_conn, DbPool},
|
utils::{functions::coalesce, get_conn, DbPool},
|
||||||
|
@ -73,9 +72,6 @@ impl PostReportView {
|
||||||
.and(person_actions::person_id.eq(my_person_id)),
|
.and(person_actions::person_id.eq(my_person_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let post_aggregates_join =
|
|
||||||
post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id));
|
|
||||||
|
|
||||||
let resolver_join = aliases::person2.on(post_report::resolver_id.eq(resolver_id.nullable()));
|
let resolver_join = aliases::person2.on(post_report::resolver_id.eq(resolver_id.nullable()));
|
||||||
|
|
||||||
post_report::table
|
post_report::table
|
||||||
|
@ -88,7 +84,6 @@ impl PostReportView {
|
||||||
.left_join(local_user_join)
|
.left_join(local_user_join)
|
||||||
.left_join(post_actions_join)
|
.left_join(post_actions_join)
|
||||||
.left_join(person_actions_join)
|
.left_join(person_actions_join)
|
||||||
.inner_join(post_aggregates_join)
|
|
||||||
.left_join(resolver_join)
|
.left_join(resolver_join)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,10 +121,9 @@ impl PostReportView {
|
||||||
person_actions::blocked.nullable().is_not_null(),
|
person_actions::blocked.nullable().is_not_null(),
|
||||||
post_actions::like_score.nullable(),
|
post_actions::like_score.nullable(),
|
||||||
coalesce(
|
coalesce(
|
||||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
post::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
post_aggregates::comments,
|
post::comments,
|
||||||
),
|
),
|
||||||
post_aggregates::all_columns,
|
|
||||||
aliases::person2.fields(person::all_columns.nullable()),
|
aliases::person2.fields(person::all_columns.nullable()),
|
||||||
))
|
))
|
||||||
.first(conn)
|
.first(conn)
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::structs::SiteView;
|
||||||
use diesel::{ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl, SelectableHelper};
|
use diesel::{ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl, SelectableHelper};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
schema::{local_site, local_site_rate_limit, site, site_aggregates},
|
schema::{local_site, local_site_rate_limit, site},
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
|
@ -16,7 +16,6 @@ impl SiteView {
|
||||||
.inner_join(
|
.inner_join(
|
||||||
local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)),
|
local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)),
|
||||||
)
|
)
|
||||||
.inner_join(site_aggregates::table)
|
|
||||||
.select(Self::as_select())
|
.select(Self::as_select())
|
||||||
.first(conn)
|
.first(conn)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -164,7 +164,7 @@ mod tests {
|
||||||
let sara_post_vote_form = PostLikeForm::new(inserted_post.id, inserted_sara.id, -1);
|
let sara_post_vote_form = PostLikeForm::new(inserted_post.id, inserted_sara.id, -1);
|
||||||
PostLike::like(pool, &sara_post_vote_form).await?;
|
PostLike::like(pool, &sara_post_vote_form).await?;
|
||||||
|
|
||||||
let expected_post_vote_views = [
|
let mut expected_post_vote_views = [
|
||||||
VoteView {
|
VoteView {
|
||||||
creator: inserted_sara.clone(),
|
creator: inserted_sara.clone(),
|
||||||
creator_banned_from_community: false,
|
creator_banned_from_community: false,
|
||||||
|
@ -176,6 +176,8 @@ mod tests {
|
||||||
score: 1,
|
score: 1,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
expected_post_vote_views[1].creator.post_count = 1;
|
||||||
|
expected_post_vote_views[1].creator.comment_count = 1;
|
||||||
|
|
||||||
let read_post_vote_views = VoteView::list_for_post(pool, inserted_post.id, None, None).await?;
|
let read_post_vote_views = VoteView::list_for_post(pool, inserted_post.id, None, None).await?;
|
||||||
assert_eq!(read_post_vote_views, expected_post_vote_views);
|
assert_eq!(read_post_vote_views, expected_post_vote_views);
|
||||||
|
@ -196,7 +198,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
CommentLike::like(pool, &sara_comment_vote_form).await?;
|
CommentLike::like(pool, &sara_comment_vote_form).await?;
|
||||||
|
|
||||||
let expected_comment_vote_views = [
|
let mut expected_comment_vote_views = [
|
||||||
VoteView {
|
VoteView {
|
||||||
creator: inserted_timmy.clone(),
|
creator: inserted_timmy.clone(),
|
||||||
creator_banned_from_community: false,
|
creator_banned_from_community: false,
|
||||||
|
@ -208,6 +210,8 @@ mod tests {
|
||||||
score: 1,
|
score: 1,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
expected_comment_vote_views[0].creator.post_count = 1;
|
||||||
|
expected_comment_vote_views[0].creator.comment_count = 1;
|
||||||
|
|
||||||
let read_comment_vote_views =
|
let read_comment_vote_views =
|
||||||
VoteView::list_for_comment(pool, inserted_comment.id, None, None).await?;
|
VoteView::list_for_comment(pool, inserted_comment.id, None, None).await?;
|
||||||
|
|
|
@ -15,14 +15,17 @@ use diesel::{
|
||||||
Queryable,
|
Queryable,
|
||||||
Selectable,
|
Selectable,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
aliases::{creator_community_actions, creator_local_user, person1},
|
||||||
|
impls::comment::comment_select_remove_deletes,
|
||||||
|
impls::community::community_follower_select_subscribed_type,
|
||||||
|
impls::local_user::local_user_can_mod,
|
||||||
|
schema::{comment, comment_actions, community_actions, local_user, person, person_actions},
|
||||||
|
utils::functions::coalesce,
|
||||||
|
Person1AliasAllColumnsTuple,
|
||||||
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::{
|
|
||||||
CommentAggregates,
|
|
||||||
CommunityAggregates,
|
|
||||||
PersonAggregates,
|
|
||||||
PostAggregates,
|
|
||||||
SiteAggregates,
|
|
||||||
},
|
|
||||||
source::{
|
source::{
|
||||||
comment::Comment,
|
comment::Comment,
|
||||||
comment_reply::CommentReply,
|
comment_reply::CommentReply,
|
||||||
|
@ -36,7 +39,6 @@ use lemmy_db_schema::{
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
local_site_rate_limit::LocalSiteRateLimit,
|
local_site_rate_limit::LocalSiteRateLimit,
|
||||||
local_user::LocalUser,
|
local_user::LocalUser,
|
||||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
|
||||||
mod_log::{
|
mod_log::{
|
||||||
admin::{
|
admin::{
|
||||||
AdminAllowInstance,
|
AdminAllowInstance,
|
||||||
|
@ -73,16 +75,6 @@ use lemmy_db_schema::{
|
||||||
},
|
},
|
||||||
SubscribedType,
|
SubscribedType,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "full")]
|
|
||||||
use lemmy_db_schema::{
|
|
||||||
aliases::{creator_community_actions, creator_local_user, person1},
|
|
||||||
impls::comment::comment_select_remove_deletes,
|
|
||||||
impls::community::community_follower_select_subscribed_type,
|
|
||||||
impls::local_user::local_user_can_mod,
|
|
||||||
schema::{comment, comment_actions, community_actions, local_user, person, person_actions},
|
|
||||||
utils::functions::coalesce,
|
|
||||||
Person1AliasAllColumnsTuple,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
|
@ -101,7 +93,6 @@ pub struct CommentReportView {
|
||||||
pub community: Community,
|
pub community: Community,
|
||||||
pub creator: Person,
|
pub creator: Person,
|
||||||
pub comment_creator: Person,
|
pub comment_creator: Person,
|
||||||
pub counts: CommentAggregates,
|
|
||||||
pub creator_banned_from_community: bool,
|
pub creator_banned_from_community: bool,
|
||||||
pub creator_is_moderator: bool,
|
pub creator_is_moderator: bool,
|
||||||
pub creator_is_admin: bool,
|
pub creator_is_admin: bool,
|
||||||
|
@ -135,8 +126,6 @@ pub struct CommentView {
|
||||||
pub post: Post,
|
pub post: Post,
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
#[cfg_attr(feature = "full", diesel(embed))]
|
||||||
pub community: Community,
|
pub community: Community,
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
|
||||||
pub counts: CommentAggregates,
|
|
||||||
#[cfg_attr(feature = "full",
|
#[cfg_attr(feature = "full",
|
||||||
diesel(
|
diesel(
|
||||||
select_expression =
|
select_expression =
|
||||||
|
@ -222,7 +211,6 @@ pub struct CommentView {
|
||||||
pub struct CommentSlimView {
|
pub struct CommentSlimView {
|
||||||
pub comment: Comment,
|
pub comment: Comment,
|
||||||
pub creator: Person,
|
pub creator: Person,
|
||||||
pub counts: CommentAggregates,
|
|
||||||
pub creator_banned_from_community: bool,
|
pub creator_banned_from_community: bool,
|
||||||
pub banned_from_community: bool,
|
pub banned_from_community: bool,
|
||||||
pub creator_is_moderator: bool,
|
pub creator_is_moderator: bool,
|
||||||
|
@ -247,7 +235,6 @@ pub struct CommunityReportView {
|
||||||
pub community_report: CommunityReport,
|
pub community_report: CommunityReport,
|
||||||
pub community: Community,
|
pub community: Community,
|
||||||
pub creator: Person,
|
pub creator: Person,
|
||||||
pub counts: CommunityAggregates,
|
|
||||||
pub subscribed: SubscribedType,
|
pub subscribed: SubscribedType,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub resolver: Option<Person>,
|
pub resolver: Option<Person>,
|
||||||
|
@ -262,11 +249,7 @@ pub struct LocalUserView {
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
#[cfg_attr(feature = "full", diesel(embed))]
|
||||||
pub local_user: LocalUser,
|
pub local_user: LocalUser,
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
#[cfg_attr(feature = "full", diesel(embed))]
|
||||||
pub local_user_vote_display_mode: LocalUserVoteDisplayMode,
|
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
|
||||||
pub person: Person,
|
pub person: Person,
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
|
||||||
pub counts: PersonAggregates,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
|
@ -294,7 +277,6 @@ pub struct PostReportView {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub my_vote: Option<i16>,
|
pub my_vote: Option<i16>,
|
||||||
pub unread_comments: i64,
|
pub unread_comments: i64,
|
||||||
pub counts: PostAggregates,
|
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub resolver: Option<Person>,
|
pub resolver: Option<Person>,
|
||||||
}
|
}
|
||||||
|
@ -325,7 +307,6 @@ pub struct PostView {
|
||||||
pub banned_from_community: bool,
|
pub banned_from_community: bool,
|
||||||
pub creator_is_moderator: bool,
|
pub creator_is_moderator: bool,
|
||||||
pub creator_is_admin: bool,
|
pub creator_is_admin: bool,
|
||||||
pub counts: PostAggregates,
|
|
||||||
pub subscribed: SubscribedType,
|
pub subscribed: SubscribedType,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
/// The time when the post was saved.
|
/// The time when the post was saved.
|
||||||
|
@ -390,8 +371,6 @@ pub struct SiteView {
|
||||||
pub local_site: LocalSite,
|
pub local_site: LocalSite,
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
#[cfg_attr(feature = "full", diesel(embed))]
|
||||||
pub local_site_rate_limit: LocalSiteRateLimit,
|
pub local_site_rate_limit: LocalSiteRateLimit,
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
|
||||||
pub counts: SiteAggregates,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
@ -437,7 +416,6 @@ pub struct ReportCombinedViewInternal {
|
||||||
// Post-specific
|
// Post-specific
|
||||||
pub post_report: Option<PostReport>,
|
pub post_report: Option<PostReport>,
|
||||||
pub post: Option<Post>,
|
pub post: Option<Post>,
|
||||||
pub post_counts: Option<PostAggregates>,
|
|
||||||
pub post_unread_comments: Option<i64>,
|
pub post_unread_comments: Option<i64>,
|
||||||
pub post_saved: Option<DateTime<Utc>>,
|
pub post_saved: Option<DateTime<Utc>>,
|
||||||
pub post_read: bool,
|
pub post_read: bool,
|
||||||
|
@ -446,7 +424,6 @@ pub struct ReportCombinedViewInternal {
|
||||||
// Comment-specific
|
// Comment-specific
|
||||||
pub comment_report: Option<CommentReport>,
|
pub comment_report: Option<CommentReport>,
|
||||||
pub comment: Option<Comment>,
|
pub comment: Option<Comment>,
|
||||||
pub comment_counts: Option<CommentAggregates>,
|
|
||||||
pub comment_saved: Option<DateTime<Utc>>,
|
pub comment_saved: Option<DateTime<Utc>>,
|
||||||
pub my_comment_vote: Option<i16>,
|
pub my_comment_vote: Option<i16>,
|
||||||
// Private-message-specific
|
// Private-message-specific
|
||||||
|
@ -454,7 +431,6 @@ pub struct ReportCombinedViewInternal {
|
||||||
pub private_message: Option<PrivateMessage>,
|
pub private_message: Option<PrivateMessage>,
|
||||||
// Community-specific
|
// Community-specific
|
||||||
pub community_report: Option<CommunityReport>,
|
pub community_report: Option<CommunityReport>,
|
||||||
pub community_counts: Option<CommunityAggregates>,
|
|
||||||
// Shared
|
// Shared
|
||||||
pub report_creator: Person,
|
pub report_creator: Person,
|
||||||
pub item_creator: Option<Person>,
|
pub item_creator: Option<Person>,
|
||||||
|
@ -485,7 +461,6 @@ pub enum ReportCombinedView {
|
||||||
/// A combined person_content view
|
/// A combined person_content view
|
||||||
pub(crate) struct PersonContentCombinedViewInternal {
|
pub(crate) struct PersonContentCombinedViewInternal {
|
||||||
// Post-specific
|
// Post-specific
|
||||||
pub post_counts: PostAggregates,
|
|
||||||
pub post_unread_comments: i64,
|
pub post_unread_comments: i64,
|
||||||
pub post_saved: Option<DateTime<Utc>>,
|
pub post_saved: Option<DateTime<Utc>>,
|
||||||
pub post_read: bool,
|
pub post_read: bool,
|
||||||
|
@ -495,7 +470,6 @@ pub(crate) struct PersonContentCombinedViewInternal {
|
||||||
pub post_tags: PostTags,
|
pub post_tags: PostTags,
|
||||||
// Comment-specific
|
// Comment-specific
|
||||||
pub comment: Option<Comment>,
|
pub comment: Option<Comment>,
|
||||||
pub comment_counts: Option<CommentAggregates>,
|
|
||||||
pub comment_saved: Option<DateTime<Utc>>,
|
pub comment_saved: Option<DateTime<Utc>>,
|
||||||
pub my_comment_vote: Option<i16>,
|
pub my_comment_vote: Option<i16>,
|
||||||
// Shared
|
// Shared
|
||||||
|
@ -527,7 +501,6 @@ pub enum PersonContentCombinedView {
|
||||||
/// A combined person_saved view
|
/// A combined person_saved view
|
||||||
pub(crate) struct PersonSavedCombinedViewInternal {
|
pub(crate) struct PersonSavedCombinedViewInternal {
|
||||||
// Post-specific
|
// Post-specific
|
||||||
pub post_counts: PostAggregates,
|
|
||||||
pub post_unread_comments: i64,
|
pub post_unread_comments: i64,
|
||||||
pub post_saved: Option<DateTime<Utc>>,
|
pub post_saved: Option<DateTime<Utc>>,
|
||||||
pub post_read: bool,
|
pub post_read: bool,
|
||||||
|
@ -537,7 +510,6 @@ pub(crate) struct PersonSavedCombinedViewInternal {
|
||||||
pub post_tags: PostTags,
|
pub post_tags: PostTags,
|
||||||
// Comment-specific
|
// Comment-specific
|
||||||
pub comment: Option<Comment>,
|
pub comment: Option<Comment>,
|
||||||
pub comment_counts: Option<CommentAggregates>,
|
|
||||||
pub comment_saved: Option<DateTime<Utc>>,
|
pub comment_saved: Option<DateTime<Utc>>,
|
||||||
pub my_comment_vote: Option<i16>,
|
pub my_comment_vote: Option<i16>,
|
||||||
// Shared
|
// Shared
|
||||||
|
@ -618,8 +590,6 @@ pub struct CommunityView {
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
pub blocked: bool,
|
pub blocked: bool,
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
|
||||||
pub counts: CommunityAggregates,
|
|
||||||
#[cfg_attr(feature = "full",
|
#[cfg_attr(feature = "full",
|
||||||
diesel(
|
diesel(
|
||||||
select_expression = community_actions::received_ban.nullable().is_not_null()
|
select_expression = community_actions::received_ban.nullable().is_not_null()
|
||||||
|
@ -670,7 +640,6 @@ pub struct PersonCommentMentionView {
|
||||||
pub post: Post,
|
pub post: Post,
|
||||||
pub community: Community,
|
pub community: Community,
|
||||||
pub recipient: Person,
|
pub recipient: Person,
|
||||||
pub counts: CommentAggregates,
|
|
||||||
pub creator_banned_from_community: bool,
|
pub creator_banned_from_community: bool,
|
||||||
pub banned_from_community: bool,
|
pub banned_from_community: bool,
|
||||||
pub creator_is_moderator: bool,
|
pub creator_is_moderator: bool,
|
||||||
|
@ -699,7 +668,6 @@ pub struct PersonPostMentionView {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub image_details: Option<ImageDetails>,
|
pub image_details: Option<ImageDetails>,
|
||||||
pub recipient: Person,
|
pub recipient: Person,
|
||||||
pub counts: PostAggregates,
|
|
||||||
pub creator_banned_from_community: bool,
|
pub creator_banned_from_community: bool,
|
||||||
pub banned_from_community: bool,
|
pub banned_from_community: bool,
|
||||||
pub creator_is_moderator: bool,
|
pub creator_is_moderator: bool,
|
||||||
|
@ -731,7 +699,6 @@ pub struct CommentReplyView {
|
||||||
pub post: Post,
|
pub post: Post,
|
||||||
pub community: Community,
|
pub community: Community,
|
||||||
pub recipient: Person,
|
pub recipient: Person,
|
||||||
pub counts: CommentAggregates,
|
|
||||||
pub creator_banned_from_community: bool,
|
pub creator_banned_from_community: bool,
|
||||||
pub banned_from_community: bool,
|
pub banned_from_community: bool,
|
||||||
pub creator_is_moderator: bool,
|
pub creator_is_moderator: bool,
|
||||||
|
@ -754,8 +721,6 @@ pub struct CommentReplyView {
|
||||||
pub struct PersonView {
|
pub struct PersonView {
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
#[cfg_attr(feature = "full", diesel(embed))]
|
||||||
pub person: Person,
|
pub person: Person,
|
||||||
#[cfg_attr(feature = "full", diesel(embed))]
|
|
||||||
pub counts: PersonAggregates,
|
|
||||||
#[cfg_attr(feature = "full",
|
#[cfg_attr(feature = "full",
|
||||||
diesel(
|
diesel(
|
||||||
select_expression_type = coalesce<diesel::sql_types::Bool, Nullable<local_user::admin>, bool>,
|
select_expression_type = coalesce<diesel::sql_types::Bool, Nullable<local_user::admin>, bool>,
|
||||||
|
@ -806,7 +771,6 @@ pub struct InboxCombinedViewInternal {
|
||||||
pub person_comment_mention: Option<PersonCommentMention>,
|
pub person_comment_mention: Option<PersonCommentMention>,
|
||||||
// Person post mention
|
// Person post mention
|
||||||
pub person_post_mention: Option<PersonPostMention>,
|
pub person_post_mention: Option<PersonPostMention>,
|
||||||
pub post_counts: Option<PostAggregates>,
|
|
||||||
pub post_unread_comments: Option<i64>,
|
pub post_unread_comments: Option<i64>,
|
||||||
pub post_saved: Option<DateTime<Utc>>,
|
pub post_saved: Option<DateTime<Utc>>,
|
||||||
pub post_read: bool,
|
pub post_read: bool,
|
||||||
|
@ -820,7 +784,6 @@ pub struct InboxCombinedViewInternal {
|
||||||
pub post: Option<Post>,
|
pub post: Option<Post>,
|
||||||
pub community: Option<Community>,
|
pub community: Option<Community>,
|
||||||
pub comment: Option<Comment>,
|
pub comment: Option<Comment>,
|
||||||
pub comment_counts: Option<CommentAggregates>,
|
|
||||||
pub comment_saved: Option<DateTime<Utc>>,
|
pub comment_saved: Option<DateTime<Utc>>,
|
||||||
pub my_comment_vote: Option<i16>,
|
pub my_comment_vote: Option<i16>,
|
||||||
pub subscribed: SubscribedType,
|
pub subscribed: SubscribedType,
|
||||||
|
@ -1170,7 +1133,6 @@ pub enum ModlogCombinedView {
|
||||||
pub(crate) struct SearchCombinedViewInternal {
|
pub(crate) struct SearchCombinedViewInternal {
|
||||||
// Post-specific
|
// Post-specific
|
||||||
pub post: Option<Post>,
|
pub post: Option<Post>,
|
||||||
pub post_counts: Option<PostAggregates>,
|
|
||||||
pub post_unread_comments: Option<i64>,
|
pub post_unread_comments: Option<i64>,
|
||||||
pub post_saved: Option<DateTime<Utc>>,
|
pub post_saved: Option<DateTime<Utc>>,
|
||||||
pub post_read: bool,
|
pub post_read: bool,
|
||||||
|
@ -1180,16 +1142,12 @@ pub(crate) struct SearchCombinedViewInternal {
|
||||||
pub post_tags: PostTags,
|
pub post_tags: PostTags,
|
||||||
// // Comment-specific
|
// // Comment-specific
|
||||||
pub comment: Option<Comment>,
|
pub comment: Option<Comment>,
|
||||||
pub comment_counts: Option<CommentAggregates>,
|
|
||||||
pub comment_saved: Option<DateTime<Utc>>,
|
pub comment_saved: Option<DateTime<Utc>>,
|
||||||
pub my_comment_vote: Option<i16>,
|
pub my_comment_vote: Option<i16>,
|
||||||
// // Community-specific
|
// // Community-specific
|
||||||
pub community: Option<Community>,
|
pub community: Option<Community>,
|
||||||
pub community_counts: Option<CommunityAggregates>,
|
|
||||||
pub community_blocked: bool,
|
pub community_blocked: bool,
|
||||||
pub subscribed: SubscribedType,
|
pub subscribed: SubscribedType,
|
||||||
// Person
|
|
||||||
pub item_creator_counts: Option<PersonAggregates>,
|
|
||||||
// Shared
|
// Shared
|
||||||
pub item_creator: Option<Person>,
|
pub item_creator: Option<Person>,
|
||||||
pub item_creator_is_admin: bool,
|
pub item_creator_is_admin: bool,
|
||||||
|
|
|
@ -482,9 +482,9 @@ fn create_post_items(posts: Vec<PostView>, settings: &Settings) -> LemmyResult<V
|
||||||
&p.creator.name,
|
&p.creator.name,
|
||||||
community_url,
|
community_url,
|
||||||
&p.community.name,
|
&p.community.name,
|
||||||
p.counts.score,
|
p.post.score,
|
||||||
post_url,
|
post_url,
|
||||||
p.counts.comments);
|
p.post.comments);
|
||||||
|
|
||||||
// If its a url post, add it to the description
|
// If its a url post, add it to the description
|
||||||
// and see if we can parse it as a media enclosure.
|
// and see if we can parse it as a media enclosure.
|
||||||
|
|
|
@ -59,12 +59,12 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
|
||||||
protocols: Some(vec!["activitypub".to_string()]),
|
protocols: Some(vec!["activitypub".to_string()]),
|
||||||
usage: Some(NodeInfoUsage {
|
usage: Some(NodeInfoUsage {
|
||||||
users: Some(NodeInfoUsers {
|
users: Some(NodeInfoUsers {
|
||||||
total: Some(site_view.counts.users),
|
total: Some(site_view.local_site.users),
|
||||||
active_halfyear: Some(site_view.counts.users_active_half_year),
|
active_halfyear: Some(site_view.local_site.users_active_half_year),
|
||||||
active_month: Some(site_view.counts.users_active_month),
|
active_month: Some(site_view.local_site.users_active_month),
|
||||||
}),
|
}),
|
||||||
local_posts: Some(site_view.counts.posts),
|
local_posts: Some(site_view.local_site.posts),
|
||||||
local_comments: Some(site_view.counts.comments),
|
local_comments: Some(site_view.local_site.comments),
|
||||||
}),
|
}),
|
||||||
open_registrations,
|
open_registrations,
|
||||||
services: Some(NodeInfoServices {
|
services: Some(NodeInfoServices {
|
||||||
|
|
|
@ -207,17 +207,15 @@ async fn process_ranks_in_batches(
|
||||||
// Raw `sql_query` is used as a performance optimization - Diesel does not support doing this
|
// Raw `sql_query` is used as a performance optimization - Diesel does not support doing this
|
||||||
// in a single query (neither as a CTE, nor using a subquery)
|
// in a single query (neither as a CTE, nor using a subquery)
|
||||||
let updated_rows = sql_query(format!(
|
let updated_rows = sql_query(format!(
|
||||||
r#"WITH batch AS (SELECT a.{id_column}
|
r#"WITH batch AS (SELECT a.id
|
||||||
FROM {aggregates_table} a
|
FROM {table_name} a
|
||||||
WHERE a.published > $1 AND ({where_clause})
|
WHERE a.published > $1 AND ({where_clause})
|
||||||
ORDER BY a.published
|
ORDER BY a.published
|
||||||
LIMIT $2
|
LIMIT $2
|
||||||
FOR UPDATE SKIP LOCKED)
|
FOR UPDATE SKIP LOCKED)
|
||||||
UPDATE {aggregates_table} a {set_clause}
|
UPDATE {table_name} a {set_clause}
|
||||||
FROM batch WHERE a.{id_column} = batch.{id_column} RETURNING a.published;
|
FROM batch WHERE a.id = batch.id RETURNING a.published;
|
||||||
"#,
|
"#,
|
||||||
id_column = format_args!("{table_name}_id"),
|
|
||||||
aggregates_table = format_args!("{table_name}_aggregates"),
|
|
||||||
))
|
))
|
||||||
.bind::<Timestamptz, _>(previous_batch_last_published)
|
.bind::<Timestamptz, _>(previous_batch_last_published)
|
||||||
.bind::<Integer, _>(update_batch_size)
|
.bind::<Integer, _>(update_batch_size)
|
||||||
|
@ -247,20 +245,20 @@ async fn process_post_aggregates_ranks_in_batches(conn: &mut AsyncPgConnection)
|
||||||
let mut previous_batch_result = Some(process_start_time);
|
let mut previous_batch_result = Some(process_start_time);
|
||||||
while let Some(previous_batch_last_published) = previous_batch_result {
|
while let Some(previous_batch_last_published) = previous_batch_result {
|
||||||
let updated_rows = sql_query(
|
let updated_rows = sql_query(
|
||||||
r#"WITH batch AS (SELECT pa.post_id
|
r#"WITH batch AS (SELECT pa.id
|
||||||
FROM post_aggregates pa
|
FROM post pa
|
||||||
WHERE pa.published > $1
|
WHERE pa.published > $1
|
||||||
AND (pa.hot_rank != 0 OR pa.hot_rank_active != 0)
|
AND (pa.hot_rank != 0 OR pa.hot_rank_active != 0)
|
||||||
ORDER BY pa.published
|
ORDER BY pa.published
|
||||||
LIMIT $2
|
LIMIT $2
|
||||||
FOR UPDATE SKIP LOCKED)
|
FOR UPDATE SKIP LOCKED)
|
||||||
UPDATE post_aggregates pa
|
UPDATE post pa
|
||||||
SET hot_rank = r.hot_rank(pa.score, pa.published),
|
SET hot_rank = r.hot_rank(pa.score, pa.published),
|
||||||
hot_rank_active = r.hot_rank(pa.score, pa.newest_comment_time_necro),
|
hot_rank_active = r.hot_rank(pa.score, pa.newest_comment_time_necro),
|
||||||
scaled_rank = r.scaled_rank(pa.score, pa.published, ca.interactions_month)
|
scaled_rank = r.scaled_rank(pa.score, pa.published, ca.interactions_month)
|
||||||
FROM batch, community_aggregates ca
|
FROM batch, community ca
|
||||||
WHERE pa.post_id = batch.post_id
|
WHERE pa.id = batch.id
|
||||||
AND pa.community_id = ca.community_id
|
AND pa.community_id = ca.id
|
||||||
RETURNING pa.published;
|
RETURNING pa.published;
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
|
@ -368,16 +366,16 @@ async fn active_counts(pool: &mut DbPool<'_>) -> LemmyResult<()> {
|
||||||
|
|
||||||
for (full_form, abbr) in &intervals {
|
for (full_form, abbr) in &intervals {
|
||||||
let update_site_stmt = format!(
|
let update_site_stmt = format!(
|
||||||
"update site_aggregates set users_active_{} = (select r.site_aggregates_activity('{}')) where site_id = 1",
|
"update local_site set users_active_{} = (select r.site_aggregates_activity('{}')) where site_id = 1",
|
||||||
abbr, full_form
|
abbr, full_form
|
||||||
);
|
);
|
||||||
sql_query(update_site_stmt).execute(&mut conn).await?;
|
sql_query(update_site_stmt).execute(&mut conn).await?;
|
||||||
|
|
||||||
let update_community_stmt = format!("update community_aggregates ca set users_active_{} = mv.count_ from r.community_aggregates_activity('{}') mv where ca.community_id = mv.community_id_", abbr, full_form);
|
let update_community_stmt = format!("update community ca set users_active_{} = mv.count_ from r.community_aggregates_activity('{}') mv where ca.id = mv.community_id_", abbr, full_form);
|
||||||
sql_query(update_community_stmt).execute(&mut conn).await?;
|
sql_query(update_community_stmt).execute(&mut conn).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let update_interactions_stmt = "update community_aggregates ca set interactions_month = mv.count_ from r.community_aggregates_interactions('1 month') mv where ca.community_id = mv.community_id_";
|
let update_interactions_stmt = "update community ca set interactions_month = mv.count_ from r.community_aggregates_interactions('1 month') mv where ca.id = mv.community_id_";
|
||||||
sql_query(update_interactions_stmt)
|
sql_query(update_interactions_stmt)
|
||||||
.execute(&mut conn)
|
.execute(&mut conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -30,6 +30,9 @@ pub const CACHE_DURATION_FEDERATION: Duration = Duration::from_millis(500);
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
pub const CACHE_DURATION_FEDERATION: Duration = Duration::from_secs(60);
|
pub const CACHE_DURATION_FEDERATION: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub const CACHE_DURATION_API: Duration = Duration::from_secs(0);
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
pub const CACHE_DURATION_API: Duration = Duration::from_secs(1);
|
pub const CACHE_DURATION_API: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
pub const MAX_COMMENT_DEPTH_LIMIT: usize = 50;
|
pub const MAX_COMMENT_DEPTH_LIMIT: usize = 50;
|
||||||
|
|
351
migrations/2025-03-04-105516_remove-aggregate-tables/down.sql
Normal file
351
migrations/2025-03-04-105516_remove-aggregate-tables/down.sql
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
-- move comment_aggregates back into separate table
|
||||||
|
CREATE TABLE comment_aggregates (
|
||||||
|
comment_id int PRIMARY KEY NOT NULL REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
score bigint NOT NULL DEFAULT 0,
|
||||||
|
upvotes bigint NOT NULL DEFAULT 0,
|
||||||
|
downvotes bigint NOT NULL DEFAULT 0,
|
||||||
|
published timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
child_count integer NOT NULL DEFAULT 0,
|
||||||
|
hot_rank double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
controversy_rank double precision NOT NULL DEFAULT 0,
|
||||||
|
report_count smallint NOT NULL DEFAULT 0,
|
||||||
|
unresolved_report_count smallint NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO comment_aggregates
|
||||||
|
SELECT
|
||||||
|
id AS comment_id,
|
||||||
|
score,
|
||||||
|
upvotes,
|
||||||
|
downvotes,
|
||||||
|
published,
|
||||||
|
child_count,
|
||||||
|
hot_rank,
|
||||||
|
controversy_rank,
|
||||||
|
report_count,
|
||||||
|
unresolved_report_count
|
||||||
|
FROM
|
||||||
|
comment;
|
||||||
|
|
||||||
|
ALTER TABLE comment
|
||||||
|
DROP COLUMN score,
|
||||||
|
DROP COLUMN upvotes,
|
||||||
|
DROP COLUMN downvotes,
|
||||||
|
DROP COLUMN child_count,
|
||||||
|
DROP COLUMN hot_rank,
|
||||||
|
DROP COLUMN controversy_rank,
|
||||||
|
DROP COLUMN report_count,
|
||||||
|
DROP COLUMN unresolved_report_count;
|
||||||
|
|
||||||
|
CREATE INDEX idx_comment_aggregates_controversy ON comment_aggregates USING btree (controversy_rank DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_comment_aggregates_hot ON comment_aggregates USING btree (hot_rank DESC, score DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_comment_aggregates_nonzero_hotrank ON comment_aggregates USING btree (published)
|
||||||
|
WHERE (hot_rank <> (0)::double precision);
|
||||||
|
|
||||||
|
CREATE INDEX idx_comment_aggregates_published ON comment_aggregates USING btree (published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_comment_aggregates_score ON comment_aggregates USING btree (score DESC);
|
||||||
|
|
||||||
|
-- move comment_aggregates back into separate table
|
||||||
|
CREATE TABLE post_aggregates (
|
||||||
|
post_id int PRIMARY KEY NOT NULL REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
comments bigint NOT NULL DEFAULT 0,
|
||||||
|
score bigint NOT NULL DEFAULT 0,
|
||||||
|
upvotes bigint NOT NULL DEFAULT 0,
|
||||||
|
downvotes bigint NOT NULL DEFAULT 0,
|
||||||
|
published timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
newest_comment_time_necro timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
newest_comment_time timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
featured_community boolean NOT NULL DEFAULT FALSE,
|
||||||
|
featured_local boolean NOT NULL DEFAULT FALSE,
|
||||||
|
hot_rank double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
hot_rank_active double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
community_id integer NOT NULL REFERENCES community (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
creator_id integer NOT NULL REFERENCES person (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
controversy_rank double precision NOT NULL DEFAULT 0,
|
||||||
|
instance_id integer NOT NULL REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
scaled_rank double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
report_count smallint NOT NULL DEFAULT 0,
|
||||||
|
unresolved_report_count smallint NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO post_aggregates
|
||||||
|
SELECT
|
||||||
|
id AS post_id,
|
||||||
|
comments,
|
||||||
|
score,
|
||||||
|
upvotes,
|
||||||
|
downvotes,
|
||||||
|
published,
|
||||||
|
newest_comment_time_necro,
|
||||||
|
newest_comment_time,
|
||||||
|
featured_community,
|
||||||
|
featured_local,
|
||||||
|
hot_rank,
|
||||||
|
hot_rank_active,
|
||||||
|
community_id,
|
||||||
|
creator_id,
|
||||||
|
controversy_rank,
|
||||||
|
instance_id,
|
||||||
|
scaled_rank,
|
||||||
|
report_count,
|
||||||
|
unresolved_report_count
|
||||||
|
FROM
|
||||||
|
post;
|
||||||
|
|
||||||
|
ALTER TABLE post
|
||||||
|
DROP COLUMN comments,
|
||||||
|
DROP COLUMN score,
|
||||||
|
DROP COLUMN upvotes,
|
||||||
|
DROP COLUMN downvotes,
|
||||||
|
DROP COLUMN newest_comment_time_necro,
|
||||||
|
DROP COLUMN newest_comment_time,
|
||||||
|
DROP COLUMN hot_rank,
|
||||||
|
DROP COLUMN hot_rank_active,
|
||||||
|
DROP COLUMN controversy_rank,
|
||||||
|
DROP COLUMN instance_id,
|
||||||
|
DROP COLUMN scaled_rank,
|
||||||
|
DROP COLUMN report_count,
|
||||||
|
DROP COLUMN unresolved_report_count;
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_active ON post_aggregates USING btree (community_id, featured_local DESC, hot_rank_active DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_controversy ON post_aggregates USING btree (community_id, featured_local DESC, controversy_rank DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_hot ON post_aggregates USING btree (community_id, featured_local DESC, hot_rank DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_most_comments ON post_aggregates USING btree (community_id, featured_local DESC, comments DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_newest_comment_time ON post_aggregates USING btree (community_id, featured_local DESC, newest_comment_time DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_newest_comment_time_necro ON post_aggregates USING btree (community_id, featured_local DESC, newest_comment_time_necro DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_published ON post_aggregates USING btree (community_id, featured_local DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_published_asc ON post_aggregates USING btree (community_id, featured_local DESC, reverse_timestamp_sort (published) DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_scaled ON post_aggregates USING btree (community_id, featured_local DESC, scaled_rank DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_community_score ON post_aggregates USING btree (community_id, featured_local DESC, score DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_active ON post_aggregates USING btree (community_id, featured_community DESC, hot_rank_active DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_controversy ON post_aggregates USING btree (community_id, featured_community DESC, controversy_rank DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_hot ON post_aggregates USING btree (community_id, featured_community DESC, hot_rank DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_most_comments ON post_aggregates USING btree (community_id, featured_community DESC, comments DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_newest_comment_time ON post_aggregates USING btree (community_id, featured_community DESC, newest_comment_time DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_newest_comment_time_necr ON post_aggregates USING btree (community_id, featured_community DESC, newest_comment_time_necro DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_published ON post_aggregates USING btree (community_id, featured_community DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_published_asc ON post_aggregates USING btree (community_id, featured_community DESC, reverse_timestamp_sort (published) DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_scaled ON post_aggregates USING btree (community_id, featured_community DESC, scaled_rank DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_community_score ON post_aggregates USING btree (community_id, featured_community DESC, score DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_active ON post_aggregates USING btree (featured_local DESC, hot_rank_active DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_controversy ON post_aggregates USING btree (featured_local DESC, controversy_rank DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_hot ON post_aggregates USING btree (featured_local DESC, hot_rank DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_most_comments ON post_aggregates USING btree (featured_local DESC, comments DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_newest_comment_time ON post_aggregates USING btree (featured_local DESC, newest_comment_time DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_newest_comment_time_necro ON post_aggregates USING btree (featured_local DESC, newest_comment_time_necro DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_published ON post_aggregates USING btree (featured_local DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_published_asc ON post_aggregates USING btree (featured_local DESC, reverse_timestamp_sort (published) DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_scaled ON post_aggregates USING btree (featured_local DESC, scaled_rank DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_featured_local_score ON post_aggregates USING btree (featured_local DESC, score DESC, published DESC, post_id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_nonzero_hotrank ON post_aggregates USING btree (published DESC)
|
||||||
|
WHERE ((hot_rank <> (0)::double precision) OR (hot_rank_active <> (0)::double precision));
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_published ON post_aggregates USING btree (published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_aggregates_published_asc ON post_aggregates USING btree (reverse_timestamp_sort (published) DESC);
|
||||||
|
|
||||||
|
DROP INDEX idx_post_featured_community_published_asc;
|
||||||
|
|
||||||
|
DROP INDEX idx_post_featured_local_published;
|
||||||
|
|
||||||
|
DROP INDEX idx_post_featured_local_published_asc;
|
||||||
|
|
||||||
|
DROP INDEX idx_post_published;
|
||||||
|
|
||||||
|
DROP INDEX idx_post_published_asc;
|
||||||
|
|
||||||
|
DROP INDEX idx_search_combined_score;
|
||||||
|
|
||||||
|
-- move community_aggregates back into separate table
|
||||||
|
CREATE TABLE community_aggregates (
|
||||||
|
community_id int PRIMARY KEY NOT NULL REFERENCES COMMunity ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
subscribers bigint NOT NULL DEFAULT 0,
|
||||||
|
posts bigint NOT NULL DEFAULT 0,
|
||||||
|
comments bigint NOT NULL DEFAULT 0,
|
||||||
|
published timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
|
users_active_day bigint NOT NULL DEFAULT 0,
|
||||||
|
users_active_week bigint NOT NULL DEFAULT 0,
|
||||||
|
users_active_month bigint NOT NULL DEFAULT 0,
|
||||||
|
users_active_half_year bigint NOT NULL DEFAULT 0,
|
||||||
|
hot_rank double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
subscribers_local bigint NOT NULL DEFAULT 0,
|
||||||
|
report_count smallint NOT NULL DEFAULT 0,
|
||||||
|
unresolved_report_count smallint NOT NULL DEFAULT 0,
|
||||||
|
interactions_month bigint NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO community_aggregates
|
||||||
|
SELECT
|
||||||
|
id AS comment_id,
|
||||||
|
subscribers,
|
||||||
|
posts,
|
||||||
|
comments,
|
||||||
|
published,
|
||||||
|
users_active_day,
|
||||||
|
users_active_week,
|
||||||
|
users_active_month,
|
||||||
|
users_active_half_year,
|
||||||
|
hot_rank,
|
||||||
|
subscribers_local,
|
||||||
|
report_count,
|
||||||
|
unresolved_report_count,
|
||||||
|
interactions_month
|
||||||
|
FROM
|
||||||
|
community;
|
||||||
|
|
||||||
|
ALTER TABLE community
|
||||||
|
DROP COLUMN subscribers,
|
||||||
|
DROP COLUMN posts,
|
||||||
|
DROP COLUMN comments,
|
||||||
|
DROP COLUMN users_active_day,
|
||||||
|
DROP COLUMN users_active_week,
|
||||||
|
DROP COLUMN users_active_month,
|
||||||
|
DROP COLUMN users_active_half_year,
|
||||||
|
DROP COLUMN hot_rank,
|
||||||
|
DROP COLUMN subscribers_local,
|
||||||
|
DROP COLUMN report_count,
|
||||||
|
DROP COLUMN unresolved_report_count,
|
||||||
|
DROP COLUMN interactions_month,
|
||||||
|
ALTER CONSTRAINT community_instance_id_fkey NOT DEFERRABLE INITIALLY IMMEDIATE;
|
||||||
|
|
||||||
|
CREATE INDEX idx_community_aggregates_hot ON public.community_aggregates USING btree (hot_rank DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_community_aggregates_nonzero_hotrank ON public.community_aggregates USING btree (published)
|
||||||
|
WHERE (hot_rank <> (0)::double precision);
|
||||||
|
|
||||||
|
CREATE INDEX idx_community_aggregates_published ON public.community_aggregates USING btree (published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_community_aggregates_subscribers ON public.community_aggregates USING btree (subscribers DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_community_aggregates_users_active_month ON public.community_aggregates USING btree (users_active_month DESC);
|
||||||
|
|
||||||
|
-- move person_aggregates back into separate table
|
||||||
|
CREATE TABLE person_aggregates (
|
||||||
|
person_id int PRIMARY KEY NOT NULL REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
post_count bigint NOT NULL DEFAULT 0,
|
||||||
|
post_score bigint NOT NULL DEFAULT 0,
|
||||||
|
comment_count bigint NOT NULL DEFAULT 0,
|
||||||
|
comment_score bigint NOT NULL DEFAULT 0,
|
||||||
|
published timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO person_aggregates
|
||||||
|
SELECT
|
||||||
|
id AS person_id,
|
||||||
|
post_count,
|
||||||
|
post_score,
|
||||||
|
comment_count,
|
||||||
|
comment_score,
|
||||||
|
published
|
||||||
|
FROM
|
||||||
|
person;
|
||||||
|
|
||||||
|
ALTER TABLE person
|
||||||
|
DROP COLUMN post_count,
|
||||||
|
DROP COLUMN post_score,
|
||||||
|
DROP COLUMN comment_count,
|
||||||
|
DROP COLUMN comment_score;
|
||||||
|
|
||||||
|
CREATE INDEX idx_person_aggregates_comment_score ON public.person_aggregates USING btree (comment_score DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_person_aggregates_person ON public.person_aggregates USING btree (person_id);
|
||||||
|
|
||||||
|
-- move site_aggregates back into separate table
|
||||||
|
CREATE TABLE site_aggregates (
|
||||||
|
site_id int PRIMARY KEY NOT NULL REFERENCES site ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
users bigint NOT NULL DEFAULT 1,
|
||||||
|
posts bigint NOT NULL DEFAULT 0,
|
||||||
|
comments bigint NOT NULL DEFAULT 0,
|
||||||
|
communities bigint NOT NULL DEFAULT 0,
|
||||||
|
users_active_day bigint NOT NULL DEFAULT 0,
|
||||||
|
users_active_week bigint NOT NULL DEFAULT 0,
|
||||||
|
users_active_month bigint NOT NULL DEFAULT 0,
|
||||||
|
users_active_half_year bigint NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO site_aggregates
|
||||||
|
SELECT
|
||||||
|
id AS site_id,
|
||||||
|
users,
|
||||||
|
posts,
|
||||||
|
comments,
|
||||||
|
communities,
|
||||||
|
users_active_day,
|
||||||
|
users_active_week,
|
||||||
|
users_active_month,
|
||||||
|
users_active_half_year
|
||||||
|
FROM
|
||||||
|
local_site;
|
||||||
|
|
||||||
|
ALTER TABLE local_site
|
||||||
|
DROP COLUMN users,
|
||||||
|
DROP COLUMN posts,
|
||||||
|
DROP COLUMN comments,
|
||||||
|
DROP COLUMN communities,
|
||||||
|
DROP COLUMN users_active_day,
|
||||||
|
DROP COLUMN users_active_week,
|
||||||
|
DROP COLUMN users_active_month,
|
||||||
|
DROP COLUMN users_active_half_year;
|
||||||
|
|
||||||
|
-- move local_user_vote_display_mode back into separate table
|
||||||
|
CREATE TABLE local_user_vote_display_mode (
|
||||||
|
local_user_id int PRIMARY KEY NOT NULL REFERENCES local_user ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
score boolean NOT NULL DEFAULT FALSE,
|
||||||
|
upvotes boolean NOT NULL DEFAULT TRUE,
|
||||||
|
downvotes boolean NOT NULL DEFAULT TRUE,
|
||||||
|
upvote_percentage boolean NOT NULL DEFAULT FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO local_user_vote_display_mode
|
||||||
|
SELECT
|
||||||
|
id AS local_user_id,
|
||||||
|
show_score AS score,
|
||||||
|
show_upvotes AS upvotes,
|
||||||
|
show_downvotes AS downvotes,
|
||||||
|
show_upvote_percentage AS upvote_percentage
|
||||||
|
FROM
|
||||||
|
local_user;
|
||||||
|
|
||||||
|
ALTER TABLE local_user
|
||||||
|
DROP COLUMN show_score,
|
||||||
|
DROP COLUMN show_upvotes,
|
||||||
|
DROP COLUMN show_downvotes,
|
||||||
|
DROP COLUMN show_upvote_percentage;
|
||||||
|
|
||||||
|
CREATE INDEX idx_search_combined_score ON public.search_combined USING btree (score DESC, id DESC);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX idx_site_aggregates_1_row_only ON public.site_aggregates USING btree ((TRUE));
|
||||||
|
|
261
migrations/2025-03-04-105516_remove-aggregate-tables/up.sql
Normal file
261
migrations/2025-03-04-105516_remove-aggregate-tables/up.sql
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
-- merge comment_aggregates into comment table
|
||||||
|
ALTER TABLE comment
|
||||||
|
ADD COLUMN score bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN upvotes bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN downvotes bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN child_count integer NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN hot_rank double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
ADD COLUMN controversy_rank double precision NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN report_count smallint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
comment
|
||||||
|
SET
|
||||||
|
score = ca.score,
|
||||||
|
upvotes = ca.upvotes,
|
||||||
|
downvotes = ca.downvotes,
|
||||||
|
child_count = ca.child_count,
|
||||||
|
hot_rank = ca.hot_rank,
|
||||||
|
controversy_rank = ca.controversy_rank,
|
||||||
|
report_count = ca.report_count,
|
||||||
|
unresolved_report_count = ca.unresolved_report_count
|
||||||
|
FROM
|
||||||
|
comment_aggregates AS ca
|
||||||
|
WHERE
|
||||||
|
comment.id = ca.comment_id;
|
||||||
|
|
||||||
|
DROP TABLE comment_aggregates;
|
||||||
|
|
||||||
|
CREATE INDEX idx_comment_controversy ON comment USING btree (controversy_rank DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_comment_hot ON comment USING btree (hot_rank DESC, score DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_comment_nonzero_hotrank ON comment USING btree (published)
|
||||||
|
WHERE (hot_rank <> (0)::double precision);
|
||||||
|
|
||||||
|
--CREATE INDEX idx_comment_published on comment USING btree (published DESC);
|
||||||
|
CREATE INDEX idx_comment_score ON comment USING btree (score DESC);
|
||||||
|
|
||||||
|
-- merge post_aggregates into post table
|
||||||
|
ALTER TABLE post
|
||||||
|
ADD COLUMN comments bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN score bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN upvotes bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN downvotes bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN newest_comment_time_necro timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
ADD COLUMN newest_comment_time timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
ADD COLUMN hot_rank double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
ADD COLUMN hot_rank_active double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
ADD COLUMN controversy_rank double precision NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN instance_id int NOT NULL DEFAULT 0 REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
ADD COLUMN scaled_rank double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
ADD COLUMN report_count smallint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
post
|
||||||
|
SET
|
||||||
|
comments = pa.comments,
|
||||||
|
score = pa.score,
|
||||||
|
upvotes = pa.upvotes,
|
||||||
|
downvotes = pa.downvotes,
|
||||||
|
newest_comment_time_necro = pa.newest_comment_time_necro,
|
||||||
|
newest_comment_time = pa.newest_comment_time,
|
||||||
|
hot_rank = pa.hot_rank,
|
||||||
|
hot_rank_active = pa.hot_rank_active,
|
||||||
|
controversy_rank = pa.controversy_rank,
|
||||||
|
instance_id = pa.instance_id,
|
||||||
|
scaled_rank = pa.scaled_rank,
|
||||||
|
report_count = pa.report_count,
|
||||||
|
unresolved_report_count = pa.unresolved_report_count
|
||||||
|
FROM
|
||||||
|
post_aggregates AS pa
|
||||||
|
WHERE
|
||||||
|
post.id = pa.post_id;
|
||||||
|
|
||||||
|
DROP TABLE post_aggregates;
|
||||||
|
|
||||||
|
-- Note, removed `post_id DESC` from all these
|
||||||
|
CREATE INDEX idx_post_community_active ON post USING btree (community_id, featured_local DESC, hot_rank_active DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_community_controversy ON post USING btree (community_id, featured_local DESC, controversy_rank DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_community_hot ON post USING btree (community_id, featured_local DESC, hot_rank DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_community_most_comments ON post USING btree (community_id, featured_local DESC, comments DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_community_newest_comment_time ON post USING btree (community_id, featured_local DESC, newest_comment_time DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_community_newest_comment_time_necro ON post USING btree (community_id, featured_local DESC, newest_comment_time_necro DESC);
|
||||||
|
|
||||||
|
-- INDEX idx_post_community_published ON post USING btree (community_id, featured_local DESC, published DESC);
|
||||||
|
--CREATE INDEX idx_post_community_published_asc ON post USING btree (community_id, featured_local DESC, reverse_timestamp_sort (published) DESC);
|
||||||
|
CREATE INDEX idx_post_community_scaled ON post USING btree (community_id, featured_local DESC, scaled_rank DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_community_score ON post USING btree (community_id, featured_local DESC, score DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_community_active ON post USING btree (community_id, featured_community DESC, hot_rank_active DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_community_controversy ON post USING btree (community_id, featured_community DESC, controversy_rank DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_community_hot ON post USING btree (community_id, featured_community DESC, hot_rank DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_community_most_comments ON post USING btree (community_id, featured_community DESC, comments DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_community_newest_comment_time ON post USING btree (community_id, featured_community DESC, newest_comment_time DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_community_newest_comment_time_necr ON post USING btree (community_id, featured_community DESC, newest_comment_time_necro DESC);
|
||||||
|
|
||||||
|
--CREATE INDEX idx_post_featured_community_published ON post USING btree (community_id, featured_community DESC, published DESC);
|
||||||
|
CREATE INDEX idx_post_featured_community_published_asc ON post USING btree (community_id, featured_community DESC, reverse_timestamp_sort (published) DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_community_scaled ON post USING btree (community_id, featured_community DESC, scaled_rank DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_community_score ON post USING btree (community_id, featured_community DESC, score DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_active ON post USING btree (featured_local DESC, hot_rank_active DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_controversy ON post USING btree (featured_local DESC, controversy_rank DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_hot ON post USING btree (featured_local DESC, hot_rank DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_most_comments ON post USING btree (featured_local DESC, comments DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_newest_comment_time ON post USING btree (featured_local DESC, newest_comment_time DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_newest_comment_time_necro ON post USING btree (featured_local DESC, newest_comment_time_necro DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_published ON post USING btree (featured_local DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_published_asc ON post USING btree (featured_local DESC, reverse_timestamp_sort (published) DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_scaled ON post USING btree (featured_local DESC, scaled_rank DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_featured_local_score ON post USING btree (featured_local DESC, score DESC, published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_nonzero_hotrank ON post USING btree (published DESC)
|
||||||
|
WHERE ((hot_rank <> (0)::double precision) OR (hot_rank_active <> (0)::double precision));
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_published ON post USING btree (published DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_published_asc ON post USING btree (reverse_timestamp_sort (published) DESC);
|
||||||
|
|
||||||
|
-- merge community_aggregates into community table
|
||||||
|
ALTER TABLE community
|
||||||
|
ADD COLUMN subscribers bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN posts bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN comments bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN users_active_day bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN users_active_week bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN users_active_month bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN users_active_half_year bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN hot_rank double precision NOT NULL DEFAULT 0.0001,
|
||||||
|
ADD COLUMN subscribers_local bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN report_count smallint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN interactions_month bigint NOT NULL DEFAULT 0,
|
||||||
|
ALTER CONSTRAINT community_instance_id_fkey DEFERRABLE INITIALLY DEFERRED;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
community
|
||||||
|
SET
|
||||||
|
subscribers = ca.subscribers,
|
||||||
|
posts = ca.posts,
|
||||||
|
comments = ca.comments,
|
||||||
|
users_active_day = ca.users_active_day,
|
||||||
|
users_active_week = ca.users_active_week,
|
||||||
|
users_active_month = ca.users_active_month,
|
||||||
|
users_active_half_year = ca.users_active_half_year,
|
||||||
|
hot_rank = ca.hot_rank,
|
||||||
|
subscribers_local = ca.subscribers_local,
|
||||||
|
report_count = ca.report_count,
|
||||||
|
unresolved_report_count = ca.unresolved_report_count,
|
||||||
|
interactions_month = ca.interactions_month
|
||||||
|
FROM
|
||||||
|
community_aggregates AS ca
|
||||||
|
WHERE
|
||||||
|
community.id = ca.community_id;
|
||||||
|
|
||||||
|
DROP TABLE community_aggregates;
|
||||||
|
|
||||||
|
CREATE INDEX idx_community_hot ON public.community USING btree (hot_rank DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_community_nonzero_hotrank ON public.community USING btree (published)
|
||||||
|
WHERE (hot_rank <> (0)::double precision);
|
||||||
|
|
||||||
|
CREATE INDEX idx_community_subscribers ON public.community USING btree (subscribers DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_community_users_active_month ON public.community USING btree (users_active_month DESC);
|
||||||
|
|
||||||
|
-- merge person_aggregates into person table
|
||||||
|
ALTER TABLE person
|
||||||
|
ADD COLUMN post_count bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN post_score bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN comment_count bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN comment_score bigint NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
person
|
||||||
|
SET
|
||||||
|
post_count = pa.post_count,
|
||||||
|
post_score = pa.post_score,
|
||||||
|
comment_count = pa.comment_count,
|
||||||
|
comment_score = pa.comment_score
|
||||||
|
FROM
|
||||||
|
person_aggregates AS pa
|
||||||
|
WHERE
|
||||||
|
person.id = pa.person_id;
|
||||||
|
|
||||||
|
DROP TABLE person_aggregates;
|
||||||
|
|
||||||
|
-- merge site_aggregates into person table
|
||||||
|
ALTER TABLE local_site
|
||||||
|
ADD COLUMN users bigint NOT NULL DEFAULT 1,
|
||||||
|
ADD COLUMN posts bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN comments bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN communities bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN users_active_day bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN users_active_week bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN users_active_month bigint NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN users_active_half_year bigint NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
local_site
|
||||||
|
SET
|
||||||
|
users = sa.users,
|
||||||
|
posts = sa.posts,
|
||||||
|
comments = sa.comments,
|
||||||
|
communities = sa.communities,
|
||||||
|
users_active_day = sa.users_active_day,
|
||||||
|
users_active_week = sa.users_active_week,
|
||||||
|
users_active_month = sa.users_active_month,
|
||||||
|
users_active_half_year = sa.users_active_half_year
|
||||||
|
FROM
|
||||||
|
site_aggregates AS sa
|
||||||
|
WHERE
|
||||||
|
local_site.site_id = sa.site_id;
|
||||||
|
|
||||||
|
DROP TABLE site_aggregates;
|
||||||
|
|
||||||
|
-- merge local_user_vote_display_mode into local_user table
|
||||||
|
ALTER TABLE local_user
|
||||||
|
ADD COLUMN show_score boolean NOT NULL DEFAULT FALSE,
|
||||||
|
ADD COLUMN show_upvotes boolean NOT NULL DEFAULT TRUE,
|
||||||
|
ADD COLUMN show_downvotes boolean NOT NULL DEFAULT TRUE,
|
||||||
|
ADD COLUMN show_upvote_percentage boolean NOT NULL DEFAULT FALSE;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
local_user
|
||||||
|
SET
|
||||||
|
show_score = v.score,
|
||||||
|
show_upvotes = v.upvotes,
|
||||||
|
show_downvotes = v.downvotes,
|
||||||
|
show_upvote_percentage = v.upvote_percentage
|
||||||
|
FROM
|
||||||
|
local_user_vote_display_mode AS v
|
||||||
|
WHERE
|
||||||
|
local_user.id = v.local_user_id;
|
||||||
|
|
||||||
|
DROP TABLE local_user_vote_display_mode;
|
||||||
|
|
Loading…
Reference in a new issue