-
-
Notifications
You must be signed in to change notification settings - Fork 883
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding report_combined
table.
#5231
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before I continue, I'd like @Nutomic , @dullbananas input on this:
- This PR currently only adds the
report_combined
table, and I'm thinking the other ones should be separate PRs. Doing a combined table for modlog is going to be really large, and it might be better to do each combined table one PR at a time.
#[derive(Debug, Serialize, Deserialize, Clone, Default)] | ||
#[cfg_attr(feature = "full", derive(TS))] | ||
#[cfg_attr(feature = "full", ts(export))] | ||
/// Report a comment. | ||
pub struct CreateCommentReport { | ||
pub comment_id: CommentId, | ||
pub reason: String, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just moved these to a reports specific mod.
pub struct GetReportCountResponse { | ||
#[cfg_attr(feature = "full", ts(optional))] | ||
pub community_id: Option<CommunityId>, | ||
pub comment_reports: i64, | ||
pub post_reports: i64, | ||
#[cfg_attr(feature = "full", ts(optional))] | ||
pub private_message_reports: Option<i64>, | ||
pub count: i64, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we now have a ReportsCombinedInternal::get_report_count()
, it seems pointless to get each specific count, and make the front-ends add them, but I can revert this back if necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Main changes are in here, and I've added tests for it.
#[cfg_attr(feature = "full", derive(Queryable))] | ||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] | ||
/// A combined report view | ||
pub struct ReportCombinedViewInternal { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to keep this struct as organized as possible, with the specific items first, then the shared last.
-- Creates combined tables for the following: | ||
-- | ||
-- Reports: (comment, post, and private_message) | ||
-- Inbox: (Comment replies, post replies, comment mentions, post mentions, private messages) | ||
-- Modlog: (lots of types) | ||
-- Search: (community, post, comment, user, url) | ||
-- TODO not sure about these two: | ||
-- Home: (comment, post) | ||
-- Community: (comment, post) | ||
CREATE TABLE report_combined ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are all the places I can think of, to do a combined enum view: Reports, Inbox, Modlog, Profile, and Search.
I'm leaning towards not doing one for Home and Community, since showing new comments next to posts isn't going to be too useful (and the front page sorts are basically useless for comments, these combined tables can only sort by published.)
One other thing: @dullbananas can I get your assistance in using cursor pagination (using your library) instead of page / limit? It'd be a good idea to start using cursor pagination for these new combined responses. |
I agree its better to make small changes one at a time. |
post_report_id int REFERENCES post_report ON UPDATE CASCADE ON DELETE CASCADE, | ||
comment_report_id int REFERENCES comment_report ON UPDATE CASCADE ON DELETE CASCADE, | ||
private_message_report_id int REFERENCES private_message_report ON UPDATE CASCADE ON DELETE CASCADE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Foreign key columns should always have indexes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that necessary here? Shouldn't we only index for read patterns, IE published desc
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indexes on foreign key columns speed up the operations caused by deleting referenced rows or updating the id values. The PaginationCursorData query also needs those indexes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how to do that for the pagination cursor query, but I'll do that for the other columns now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized it can also be done by adding UNIQUE
individually to each column, which automatically creates the indexes. I don't know if that's better.
Also there's no different index needed for the PaginationCursorData query. The indexes you added are all it needs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized it can also be done by adding UNIQUE individually to each column, which automatically creates the indexes. I don't know if that's better.
I probably can't do that here since there are a lot of nulls repeated for each row. But I've added the indexes manually anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unique constraint allows multiple null values by default https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-UNIQUE-CONSTRAINTS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
K I'll try that instead.
see #5244 |
* add pagination cursor * store timestamp instead of id in cursor (partial) * Revert "store timestamp instead of id in cursor (partial)" This reverts commit 89359dd. * use paginated query builder
…into combined_tables_2
> { | ||
let all_joins = |query: comment_report::BoxedQuery<'a, Pg>, my_person_id: PersonId| { | ||
query | ||
impl CommentReportView { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I simplified the specific report views to only reads now, not list. The report combined view should always be used for listing reports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these read functions really needed? At least lemmy-ui doesnt seem to use them. Would also be possible to move read into ReportCombinedView
, with a single /report/read
endpoint which takes ReportCombinedId
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes they are needed IMO, the reportombinedid is pretty useless, we could even skip serializing it because it doesn't help at all. (actually I think we don't serialize it, the specific views are all that come back).
The main reason its important to keep the specific reads, is that when you ask for a comment report, you might as well get a CommentReportView
back, rather than having to get multiple types and coerce them. There's no reason to do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright
|
||
#[tokio::test] | ||
#[serial] | ||
async fn test_crud() -> LemmyResult<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I simplified and moved all the tests to ReportCombinedView
, since they need to call ReportCombinedView::list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main changes are in this file.
I made a new combined reports test, and copied all the tests from the previous views, and refactored them to work with the combined list, so everything should be fine.
src/api_routes_http.rs
Outdated
@@ -277,8 +267,12 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { | |||
.route("/save", put().to(save_comment)) | |||
.route("/list", get().to(list_comments)) | |||
.route("/report", post().to(create_comment_report)) | |||
.route("/report/resolve", put().to(resolve_comment_report)) | |||
.route("/report/list", get().to(list_comment_reports)), | |||
.route("/report/resolve", put().to(resolve_comment_report)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about removing these separate report endpoints and making a single endpoint /report/resolve
which takes ReportCombinedId
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a not a good idea, since you still need to create report for the specific item anyway. And the reportcombinedid
isn't useful for resolving.
#[cfg_attr(feature = "full", derive(DieselNewType, TS))] | ||
#[cfg_attr(feature = "full", ts(export))] | ||
/// The report combined id | ||
pub struct ReportCombinedId(i32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IDs for this table should be an implementation detail, so don't include TS in derive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll also add serde skip to that. Er maybe that won't be necessary since it never gets serialized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you can remove derive(Serialize, Deserialize)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
} else { | ||
None | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the last else
is supposed to be unreachable, then make this conversion happen before the vec is built by moving this function's code into a custom implementation of Queryable
for ReportCombinedView
that calls ReportCombinedViewInternal::build
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems a bit hacky, since as far as sql is concerned, those columns are nullable. I'd rather keep this conversion simple, and rely on the tests to catch any issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Silently failing is more hacky and is important to not do in this case. Changing this to a Queryable
implementation is a simple enough way to fix that, and it will also make deserialization separate from the load
function. Do it like this:
impl<ST> Queryable<ST, Pg> for ReportCombinedView
where
ReportCombinedViewInternal: Queryable<ST, Pg>,
{
type Row = ReportCombinedViewInternal::Row;
fn build(row: Self::Row) -> deserialize::Result<Self> {
let v = ReportCombinedViewInternal::build(row)?;
// body of map_to_enum function goes here, with Some and None replaced with Ok and Err(Box::new(UnexpectedNullError))
}
}
Co-authored-by: dullbananas <[email protected]>
Co-authored-by: dullbananas <[email protected]>
Co-authored-by: dullbananas <[email protected]>
Co-authored-by: dullbananas <[email protected]>
Co-authored-by: dullbananas <[email protected]>
With all these combined_tables PR's I'm working on, I'm stacking them because they use a lot of conflicting code space anyway, so I'll mark these as ready to review as each one gets finished. |
pub item_creator_banned_from_community: bool, | ||
pub item_creator_is_moderator: bool, | ||
pub item_creator_blocked: bool, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you simplify this similar to ModlogCombinedViewInternal
in the other PR, or does that not work with the db query?
pub struct ReportCombinedViewInternal {
pub post: Option<PostReportView>,
pub comment: Option<CommentReportView>,
pub private_message: Option<PrivateMessageReportView>,
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That can't work for either one. The Internal is the actual DB query that selects everything necessary, then a mapping function maps that data to the enum, which is a container for the different types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to merge.
@dullbananas can you approve as well if theres nothing else?
Context: #2444