Skip to content
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

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open

Adding report_combined table. #5231

wants to merge 41 commits into from

Conversation

dessalines
Copy link
Member

@dessalines dessalines commented Nov 26, 2024

Context: #2444

@dessalines dessalines changed the title Combined tables try 2 WIP: Combined tables try 2 Nov 26, 2024
Copy link
Member Author

@dessalines dessalines left a 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.

Comment on lines -149 to -156
#[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,
}
Copy link
Member Author

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.

Comment on lines 450 to +451
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,
Copy link
Member Author

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.

crates/db_schema/replaceable_schema/triggers.sql Outdated Show resolved Hide resolved
Copy link
Member Author

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 {
Copy link
Member Author

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.

src/api_routes_http.rs Outdated Show resolved Hide resolved
src/api_routes_http.rs Outdated Show resolved Hide resolved
@dessalines dessalines changed the title WIP: Combined tables try 2 Adding report_combined table. Nov 27, 2024
Comment on lines 1 to 10
-- 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 (
Copy link
Member Author

@dessalines dessalines Nov 27, 2024

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.)

@dessalines
Copy link
Member Author

dessalines commented Nov 27, 2024

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.

@Nutomic
Copy link
Member

Nutomic commented Nov 28, 2024

I agree its better to make small changes one at a time.

Comment on lines 14 to 16
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,
Copy link
Collaborator

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

Copy link
Member Author

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 ?

Copy link
Collaborator

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.

Copy link
Member Author

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.

Copy link
Collaborator

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.

Copy link
Member Author

@dessalines dessalines Dec 2, 2024

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

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.

@dullbananas
Copy link
Collaborator

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.

see #5244

> {
let all_joins = |query: comment_report::BoxedQuery<'a, Pg>, my_person_id: PersonId| {
query
impl CommentReportView {
Copy link
Member Author

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.

Copy link
Member

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bump

Copy link
Member Author

@dessalines dessalines Dec 10, 2024

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.

Copy link
Member

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<()> {
Copy link
Member Author

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

Copy link
Member Author

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.

@@ -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)),
Copy link
Member

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?

Copy link
Member Author

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);
Copy link
Collaborator

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

Copy link
Member Author

@dessalines dessalines Dec 8, 2024

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.

Copy link
Member

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)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

crates/db_views/src/report_combined_view.rs Outdated Show resolved Hide resolved
crates/db_views/src/report_combined_view.rs Outdated Show resolved Hide resolved
crates/db_views/src/report_combined_view.rs Outdated Show resolved Hide resolved
} else {
None
}
}
Copy link
Collaborator

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

Copy link
Member Author

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.

Copy link
Collaborator

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))
  }
}

@dessalines
Copy link
Member Author

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.

api_tests/src/post.spec.ts Outdated Show resolved Hide resolved
api_tests/src/post.spec.ts Outdated Show resolved Hide resolved
pub item_creator_banned_from_community: bool,
pub item_creator_is_moderator: bool,
pub item_creator_blocked: bool,
}
Copy link
Member

@Nutomic Nutomic Dec 12, 2024

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>,
}

Copy link
Member Author

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.

Copy link
Member

@Nutomic Nutomic left a 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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants