mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-23 03:11:32 +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
5 changed files with 100 additions and 19 deletions
|
@ -89,19 +89,19 @@ pub struct PersonMentionId(i32);
|
|||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The comment report id.
|
||||
pub struct CommentReportId(i32);
|
||||
pub struct CommentReportId(pub i32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The post report id.
|
||||
pub struct PostReportId(i32);
|
||||
pub struct PostReportId(pub i32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The private message report id.
|
||||
pub struct PrivateMessageReportId(i32);
|
||||
pub struct PrivateMessageReportId(pub i32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||
|
|
|
@ -2,6 +2,8 @@ use crate::newtypes::{CommentReportId, PostReportId, PrivateMessageReportId, Rep
|
|||
#[cfg(feature = "full")]
|
||||
use crate::schema::report_combined;
|
||||
use chrono::{DateTime, Utc};
|
||||
#[cfg(feature = "full")]
|
||||
use i_love_jesus::CursorKeysModule;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
#[cfg(feature = "full")]
|
||||
|
@ -9,10 +11,14 @@ use ts_rs::TS;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[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(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
#[cfg_attr(feature = "full", cursor_keys_module(name = report_combined_keys))]
|
||||
/// A combined reports table.
|
||||
pub struct ReportCombined {
|
||||
pub id: ReportCombinedId,
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::structs::{
|
|||
LocalUserView,
|
||||
PostReportView,
|
||||
PrivateMessageReportView,
|
||||
ReportCombinedPaginationCursor,
|
||||
ReportCombinedView,
|
||||
ReportCombinedViewInternal,
|
||||
};
|
||||
|
@ -13,8 +14,10 @@ use diesel::{
|
|||
JoinOnDsl,
|
||||
NullableExpressionMethods,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use i_love_jesus::PaginatedQueryBuilder;
|
||||
use lemmy_db_schema::{
|
||||
aliases::{self, creator_community_actions},
|
||||
newtypes::CommunityId,
|
||||
|
@ -36,8 +39,19 @@ use lemmy_db_schema::{
|
|||
private_message_report,
|
||||
report_combined,
|
||||
},
|
||||
source::community::CommunityFollower,
|
||||
utils::{actions, actions_alias, functions::coalesce, get_conn, limit_and_offset, DbPool},
|
||||
source::{
|
||||
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;
|
||||
|
||||
|
@ -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)]
|
||||
pub struct ReportCombinedQuery {
|
||||
pub community_id: Option<CommunityId>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
pub unresolved_only: bool,
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub page_back: bool,
|
||||
}
|
||||
|
||||
impl ReportCombinedQuery {
|
||||
|
@ -237,18 +286,6 @@ impl ReportCombinedQuery {
|
|||
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 !user.local_user.admin {
|
||||
query = query.filter(community_actions::became_moderator.is_not_null());
|
||||
|
@ -258,6 +295,36 @@ impl ReportCombinedQuery {
|
|||
|
||||
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?;
|
||||
|
||||
// Map the query results to the enum
|
||||
|
|
|
@ -126,6 +126,12 @@ pub struct PostReportView {
|
|||
#[cfg_attr(feature = "full", ts(export))]
|
||||
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]
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[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)
|
||||
);
|
||||
|
||||
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
|
||||
INSERT INTO report_combined (published, post_report_id)
|
||||
|
|
Loading…
Reference in a new issue