mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-12 05:54:51 +00:00
Add cursor pagination to report_combined view (#5244)
* add pagination cursor
* store timestamp instead of id in cursor (partial)
* Revert "store timestamp instead of id in cursor (partial)"
This reverts commit 89359dde4b
.
* use paginated query builder
This commit is contained in:
parent
d8dda44010
commit
588e1f6c0a
|
@ -89,19 +89,19 @@ pub struct PersonMentionId(i32);
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The comment report id.
|
/// The comment report id.
|
||||||
pub struct CommentReportId(i32);
|
pub struct CommentReportId(pub i32);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The post report id.
|
/// The post report id.
|
||||||
pub struct PostReportId(i32);
|
pub struct PostReportId(pub i32);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The private message report id.
|
/// The private message report id.
|
||||||
pub struct PrivateMessageReportId(i32);
|
pub struct PrivateMessageReportId(pub i32);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
|
|
|
@ -2,6 +2,8 @@ use crate::newtypes::{CommentReportId, PostReportId, PrivateMessageReportId, Rep
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use crate::schema::report_combined;
|
use crate::schema::report_combined;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
#[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")]
|
||||||
|
@ -9,10 +11,14 @@ use ts_rs::TS;
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
|
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Selectable, TS))]
|
#[cfg_attr(
|
||||||
|
feature = "full",
|
||||||
|
derive(Identifiable, Queryable, Selectable, TS, CursorKeysModule)
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = report_combined))]
|
#[cfg_attr(feature = "full", diesel(table_name = report_combined))]
|
||||||
#[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", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
#[cfg_attr(feature = "full", cursor_keys_module(name = report_combined_keys))]
|
||||||
/// A combined reports table.
|
/// A combined reports table.
|
||||||
pub struct ReportCombined {
|
pub struct ReportCombined {
|
||||||
pub id: ReportCombinedId,
|
pub id: ReportCombinedId,
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::structs::{
|
||||||
LocalUserView,
|
LocalUserView,
|
||||||
PostReportView,
|
PostReportView,
|
||||||
PrivateMessageReportView,
|
PrivateMessageReportView,
|
||||||
|
ReportCombinedPaginationCursor,
|
||||||
ReportCombinedView,
|
ReportCombinedView,
|
||||||
ReportCombinedViewInternal,
|
ReportCombinedViewInternal,
|
||||||
};
|
};
|
||||||
|
@ -13,8 +14,10 @@ use diesel::{
|
||||||
JoinOnDsl,
|
JoinOnDsl,
|
||||||
NullableExpressionMethods,
|
NullableExpressionMethods,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
|
SelectableHelper,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use i_love_jesus::PaginatedQueryBuilder;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aliases::{self, creator_community_actions},
|
aliases::{self, creator_community_actions},
|
||||||
newtypes::CommunityId,
|
newtypes::CommunityId,
|
||||||
|
@ -36,8 +39,19 @@ use lemmy_db_schema::{
|
||||||
private_message_report,
|
private_message_report,
|
||||||
report_combined,
|
report_combined,
|
||||||
},
|
},
|
||||||
source::community::CommunityFollower,
|
source::{
|
||||||
utils::{actions, actions_alias, functions::coalesce, get_conn, limit_and_offset, DbPool},
|
combined::report::{report_combined_keys as key, ReportCombined},
|
||||||
|
community::CommunityFollower,
|
||||||
|
},
|
||||||
|
utils::{
|
||||||
|
actions,
|
||||||
|
actions_alias,
|
||||||
|
functions::coalesce,
|
||||||
|
get_conn,
|
||||||
|
limit_and_offset,
|
||||||
|
DbPool,
|
||||||
|
ReverseTimestampKey,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
|
@ -94,12 +108,47 @@ impl ReportCombinedViewInternal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ReportCombinedPaginationCursor {
|
||||||
|
// get cursor for page that starts immediately after the given post
|
||||||
|
pub fn after_post(view: &ReportCombinedView) -> ReportCombinedPaginationCursor {
|
||||||
|
let (prefix, id) = match view {
|
||||||
|
ReportCombinedView::Comment(v) => ('C', v.comment_report.id.0),
|
||||||
|
ReportCombinedView::Post(v) => ('P', v.post_report.id.0),
|
||||||
|
ReportCombinedView::PrivateMessage(v) => ('M', v.private_message_report.id.0),
|
||||||
|
};
|
||||||
|
// hex encoding to prevent ossification
|
||||||
|
ReportCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
||||||
|
}
|
||||||
|
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
||||||
|
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||||
|
let mut query = report_combined::table
|
||||||
|
.select(ReportCombined::as_select())
|
||||||
|
.into_boxed();
|
||||||
|
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
||||||
|
let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
|
||||||
|
query = match prefix {
|
||||||
|
"C" => query.filter(report_combined::comment_report_id.eq(id)),
|
||||||
|
"P" => query.filter(report_combined::post_report_id.eq(id)),
|
||||||
|
"M" => query.filter(report_combined::private_message_report_id.eq(id)),
|
||||||
|
_ => return Err(err_msg()),
|
||||||
|
};
|
||||||
|
let token = query.first(&mut get_conn(pool).await?).await?;
|
||||||
|
|
||||||
|
Ok(PaginationCursorData(token))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PaginationCursorData(ReportCombined);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ReportCombinedQuery {
|
pub struct ReportCombinedQuery {
|
||||||
pub community_id: Option<CommunityId>,
|
pub community_id: Option<CommunityId>,
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
pub limit: Option<i64>,
|
pub limit: Option<i64>,
|
||||||
pub unresolved_only: bool,
|
pub unresolved_only: bool,
|
||||||
|
pub page_after: Option<PaginationCursorData>,
|
||||||
|
pub page_back: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReportCombinedQuery {
|
impl ReportCombinedQuery {
|
||||||
|
@ -237,18 +286,6 @@ impl ReportCombinedQuery {
|
||||||
query = query.filter(community::id.eq(community_id));
|
query = query.filter(community::id.eq(community_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If viewing all reports, order by newest, but if viewing unresolved only, show the oldest
|
|
||||||
// first (FIFO)
|
|
||||||
if options.unresolved_only {
|
|
||||||
query = query
|
|
||||||
.filter(post_report::resolved.eq(false))
|
|
||||||
.or_filter(comment_report::resolved.eq(false))
|
|
||||||
.or_filter(private_message_report::resolved.eq(false))
|
|
||||||
.order_by(report_combined::published.asc());
|
|
||||||
} else {
|
|
||||||
query = query.order_by(report_combined::published.desc());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If its not an admin, get only the ones you mod
|
// If its not an admin, get only the ones you mod
|
||||||
if !user.local_user.admin {
|
if !user.local_user.admin {
|
||||||
query = query.filter(community_actions::became_moderator.is_not_null());
|
query = query.filter(community_actions::became_moderator.is_not_null());
|
||||||
|
@ -258,6 +295,36 @@ impl ReportCombinedQuery {
|
||||||
|
|
||||||
query = query.limit(limit).offset(offset);
|
query = query.limit(limit).offset(offset);
|
||||||
|
|
||||||
|
let mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
|
let page_after = options.page_after.map(|c| c.0);
|
||||||
|
|
||||||
|
if options.page_back {
|
||||||
|
query = query.before(page_after).limit_and_offset_from_end();
|
||||||
|
} else {
|
||||||
|
query = query.after(page_after);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If viewing all reports, order by newest, but if viewing unresolved only, show the oldest
|
||||||
|
// first (FIFO)
|
||||||
|
if options.unresolved_only {
|
||||||
|
query = query
|
||||||
|
.filter(
|
||||||
|
post_report::resolved
|
||||||
|
.eq(false)
|
||||||
|
.or(comment_report::resolved.eq(false))
|
||||||
|
.or(private_message_report::resolved.eq(false)),
|
||||||
|
)
|
||||||
|
// TODO: when a `then_asc` method is added, use it here, make the id sort direction match,
|
||||||
|
// and remove the separate index; unless additional columns are added to this sort
|
||||||
|
.then_desc(ReverseTimestampKey(key::published));
|
||||||
|
} else {
|
||||||
|
query = query.then_desc(key::published);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tie breaker
|
||||||
|
query = query.then_desc(key::id);
|
||||||
|
|
||||||
let res = query.load::<ReportCombinedViewInternal>(conn).await?;
|
let res = query.load::<ReportCombinedViewInternal>(conn).await?;
|
||||||
|
|
||||||
// Map the query results to the enum
|
// Map the query results to the enum
|
||||||
|
|
|
@ -126,6 +126,12 @@ pub struct PostReportView {
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
pub struct PaginationCursor(pub String);
|
pub struct PaginationCursor(pub String);
|
||||||
|
|
||||||
|
/// like PaginationCursor but for the report_combined table
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "full", derive(ts_rs::TS))]
|
||||||
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
pub struct ReportCombinedPaginationCursor(pub String);
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||||
|
|
|
@ -17,7 +17,9 @@ CREATE TABLE report_combined (
|
||||||
UNIQUE (post_report_id, comment_report_id, private_message_report_id)
|
UNIQUE (post_report_id, comment_report_id, private_message_report_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_report_combined_published ON report_combined (published DESC);
|
CREATE INDEX idx_report_combined_published ON report_combined (published DESC, id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_report_combined_published_asc ON report_combined (reverse_timestamp_sort (published) DESC, id DESC);
|
||||||
|
|
||||||
-- Updating the history
|
-- Updating the history
|
||||||
INSERT INTO report_combined (published, post_report_id)
|
INSERT INTO report_combined (published, post_report_id)
|
||||||
|
|
Loading…
Reference in a new issue