2 aggregates::comment_aggregates::CommentAggregates,
20 comment::{Comment, CommentAlias1, CommentSaved},
21 community::{Community, CommunityFollower, CommunitySafe, CommunityUserBan},
23 user::{UserAlias1, UserSafe, UserSafeAlias1, User_},
31 use diesel::{result::Error, *};
34 #[derive(Debug, PartialEq, Serialize, Clone)]
35 pub struct CommentView {
37 pub creator: UserSafe,
38 pub recipient: Option<UserSafeAlias1>, // Left joins to comment and user
40 pub community: CommunitySafe,
41 pub counts: CommentAggregates,
42 pub creator_banned_from_community: bool, // Left Join to CommunityUserBan
43 pub subscribed: bool, // Left join to CommunityFollower
44 pub saved: bool, // Left join to CommentSaved
45 pub my_vote: Option<i16>, // Left join to CommentLike
48 type CommentViewTuple = (
51 Option<CommentAlias1>,
52 Option<UserSafeAlias1>,
56 Option<CommunityUserBan>,
57 Option<CommunityFollower>,
66 my_user_id: Option<i32>,
67 ) -> Result<Self, Error> {
68 // The left join below will return None in this case
69 let user_id_join = my_user_id.unwrap_or(-1);
79 creator_banned_from_community,
85 .inner_join(user_::table)
87 .left_join(comment_alias_1::table.on(comment_alias_1::id.nullable().eq(comment::parent_id)))
88 .left_join(user_alias_1::table.on(user_alias_1::id.eq(comment_alias_1::creator_id)))
89 .inner_join(post::table)
90 .inner_join(community::table.on(post::community_id.eq(community::id)))
91 .inner_join(comment_aggregates::table)
93 community_user_ban::table.on(
95 .eq(community_user_ban::community_id)
96 .and(community_user_ban::user_id.eq(comment::creator_id)),
100 community_follower::table.on(
102 .eq(community_follower::community_id)
103 .and(community_follower::user_id.eq(user_id_join)),
107 comment_saved::table.on(
109 .eq(comment_saved::comment_id)
110 .and(comment_saved::user_id.eq(user_id_join)),
114 comment_like::table.on(
116 .eq(comment_like::comment_id)
117 .and(comment_like::user_id.eq(user_id_join)),
121 comment::all_columns,
122 User_::safe_columns_tuple(),
123 comment_alias_1::all_columns.nullable(),
124 UserAlias1::safe_columns_tuple().nullable(),
126 Community::safe_columns_tuple(),
127 comment_aggregates::all_columns,
128 community_user_ban::all_columns.nullable(),
129 community_follower::all_columns.nullable(),
130 comment_saved::all_columns.nullable(),
131 comment_like::score.nullable(),
133 .first::<CommentViewTuple>(conn)?;
142 creator_banned_from_community: creator_banned_from_community.is_some(),
143 subscribed: subscribed.is_some(),
144 saved: saved.is_some(),
166 query_builder::BoxedSelectStatement,
167 query_source::joins::{Inner, Join, JoinOn, LeftOuter},
171 // /// TODO awful, but necessary because of the boxed join
172 pub(super) type BoxedCommentJoin<'a> = BoxedSelectStatement<
273 (Integer, Integer, BigInt, BigInt, BigInt),
274 Nullable<(Integer, Integer, Integer, Timestamp)>,
275 Nullable<(Integer, Integer, Integer, Timestamp, Nullable<Bool>)>,
276 Nullable<(Integer, Integer, Integer, Timestamp)>,
298 Join<comment::table, user_::table, Inner>,
299 diesel::expression::operators::Eq<
300 diesel::expression::nullable::Nullable<
301 comment::columns::creator_id,
303 diesel::expression::nullable::Nullable<
308 comment_alias_1::table,
311 diesel::expression::operators::Eq<
312 diesel::expression::nullable::Nullable<
313 comment_alias_1::columns::id,
315 comment::columns::parent_id,
321 diesel::expression::operators::Eq<
322 user_alias_1::columns::id,
323 comment_alias_1::columns::creator_id,
329 diesel::expression::operators::Eq<
330 diesel::expression::nullable::Nullable<comment::columns::post_id>,
331 diesel::expression::nullable::Nullable<post::columns::id>,
337 diesel::expression::operators::Eq<
338 post::columns::community_id,
339 community::columns::id,
342 comment_aggregates::table,
345 diesel::expression::operators::Eq<
346 diesel::expression::nullable::Nullable<
347 comment_aggregates::columns::comment_id,
349 diesel::expression::nullable::Nullable<comment::columns::id>,
352 community_user_ban::table,
355 diesel::expression::operators::And<
356 diesel::expression::operators::Eq<
357 community::columns::id,
358 community_user_ban::columns::community_id,
360 diesel::expression::operators::Eq<
361 community_user_ban::columns::user_id,
362 comment::columns::creator_id,
366 community_follower::table,
369 diesel::expression::operators::And<
370 diesel::expression::operators::Eq<
371 post::columns::community_id,
372 community_follower::columns::community_id,
374 diesel::expression::operators::Eq<
375 community_follower::columns::user_id,
376 diesel::expression::bound::Bound<Integer, i32>,
380 comment_saved::table,
383 diesel::expression::operators::And<
384 diesel::expression::operators::Eq<
385 comment::columns::id,
386 comment_saved::columns::comment_id,
388 diesel::expression::operators::Eq<
389 comment_saved::columns::user_id,
390 diesel::expression::bound::Bound<Integer, i32>,
397 diesel::expression::operators::And<
398 diesel::expression::operators::Eq<comment::columns::id, comment_like::columns::comment_id>,
399 diesel::expression::operators::Eq<
400 comment_like::columns::user_id,
401 diesel::expression::bound::Bound<Integer, i32>,
409 pub struct CommentQueryBuilder<'a> {
410 conn: &'a PgConnection,
411 query: join_types::BoxedCommentJoin<'a>,
412 listing_type: ListingType,
414 for_community_id: Option<i32>,
415 for_community_name: Option<String>,
416 for_post_id: Option<i32>,
417 for_creator_id: Option<i32>,
418 for_recipient_id: Option<i32>,
419 search_term: Option<String>,
426 impl<'a> CommentQueryBuilder<'a> {
427 pub fn create(conn: &'a PgConnection, my_user_id: Option<i32>) -> Self {
428 // The left join below will return None in this case
429 let user_id_join = my_user_id.unwrap_or(-1);
431 let query = comment::table
432 .inner_join(user_::table)
434 .left_join(comment_alias_1::table.on(comment_alias_1::id.nullable().eq(comment::parent_id)))
435 .left_join(user_alias_1::table.on(user_alias_1::id.eq(comment_alias_1::creator_id)))
436 .inner_join(post::table)
437 .inner_join(community::table.on(post::community_id.eq(community::id)))
438 .inner_join(comment_aggregates::table)
440 community_user_ban::table.on(
442 .eq(community_user_ban::community_id)
443 .and(community_user_ban::user_id.eq(comment::creator_id)),
447 community_follower::table.on(
449 .eq(community_follower::community_id)
450 .and(community_follower::user_id.eq(user_id_join)),
454 comment_saved::table.on(
456 .eq(comment_saved::comment_id)
457 .and(comment_saved::user_id.eq(user_id_join)),
461 comment_like::table.on(
463 .eq(comment_like::comment_id)
464 .and(comment_like::user_id.eq(user_id_join)),
468 comment::all_columns,
469 User_::safe_columns_tuple(),
470 comment_alias_1::all_columns.nullable(),
471 UserAlias1::safe_columns_tuple().nullable(),
473 Community::safe_columns_tuple(),
474 comment_aggregates::all_columns,
475 community_user_ban::all_columns.nullable(),
476 community_follower::all_columns.nullable(),
477 comment_saved::all_columns.nullable(),
478 comment_like::score.nullable(),
482 CommentQueryBuilder {
485 listing_type: ListingType::All,
486 sort: &SortType::New,
487 for_community_id: None,
488 for_community_name: None,
490 for_creator_id: None,
491 for_recipient_id: None,
500 pub fn listing_type(mut self, listing_type: ListingType) -> Self {
501 self.listing_type = listing_type;
505 pub fn sort(mut self, sort: &'a SortType) -> Self {
510 pub fn for_post_id<T: MaybeOptional<i32>>(mut self, for_post_id: T) -> Self {
511 self.for_post_id = for_post_id.get_optional();
515 pub fn for_creator_id<T: MaybeOptional<i32>>(mut self, for_creator_id: T) -> Self {
516 self.for_creator_id = for_creator_id.get_optional();
520 pub fn for_recipient_id<T: MaybeOptional<i32>>(mut self, for_recipient_id: T) -> Self {
521 self.for_creator_id = for_recipient_id.get_optional();
525 pub fn for_community_id<T: MaybeOptional<i32>>(mut self, for_community_id: T) -> Self {
526 self.for_community_id = for_community_id.get_optional();
530 pub fn for_community_name<T: MaybeOptional<String>>(mut self, for_community_name: T) -> Self {
531 self.for_community_name = for_community_name.get_optional();
535 pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
536 self.search_term = search_term.get_optional();
540 pub fn saved_only(mut self, saved_only: bool) -> Self {
541 self.saved_only = saved_only;
545 pub fn unread_only(mut self, unread_only: bool) -> Self {
546 self.unread_only = unread_only;
550 pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
551 self.page = page.get_optional();
555 pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
556 self.limit = limit.get_optional();
560 pub fn list(self) -> Result<Vec<CommentView>, Error> {
563 let mut query = self.query;
566 if let Some(for_recipient_id) = self.for_recipient_id {
568 // TODO needs lots of testing
569 .filter(user_alias_1::id.eq(for_recipient_id))
570 .filter(comment::deleted.eq(false))
571 .filter(comment::removed.eq(false));
574 if self.unread_only {
575 query = query.filter(comment::read.eq(false));
578 if let Some(for_creator_id) = self.for_creator_id {
579 query = query.filter(comment::creator_id.eq(for_creator_id));
582 if let Some(for_community_id) = self.for_community_id {
583 query = query.filter(post::community_id.eq(for_community_id));
586 if let Some(for_community_name) = self.for_community_name {
588 .filter(community::name.eq(for_community_name))
589 .filter(comment::local.eq(true));
592 if let Some(for_post_id) = self.for_post_id {
593 query = query.filter(comment::post_id.eq(for_post_id));
596 if let Some(search_term) = self.search_term {
597 query = query.filter(comment::content.ilike(fuzzy_search(&search_term)));
600 query = match self.listing_type {
601 // ListingType::Subscribed => query.filter(community_follower::subscribed.eq(true)),
602 ListingType::Subscribed => query.filter(community_follower::user_id.is_not_null()), // TODO could be this: and(community_follower::user_id.eq(user_id_join)),
603 ListingType::Local => query.filter(community::local.eq(true)),
608 query = query.filter(comment_saved::id.is_not_null());
611 query = match self.sort {
612 SortType::Hot | SortType::Active => query
613 .order_by(hot_rank(comment_aggregates::score, comment::published).desc())
614 .then_order_by(comment::published.desc()),
615 SortType::New => query.order_by(comment::published.desc()),
616 SortType::TopAll => query.order_by(comment_aggregates::score.desc()),
617 SortType::TopYear => query
618 .filter(comment::published.gt(now - 1.years()))
619 .order_by(comment_aggregates::score.desc()),
620 SortType::TopMonth => query
621 .filter(comment::published.gt(now - 1.months()))
622 .order_by(comment_aggregates::score.desc()),
623 SortType::TopWeek => query
624 .filter(comment::published.gt(now - 1.weeks()))
625 .order_by(comment_aggregates::score.desc()),
626 SortType::TopDay => query
627 .filter(comment::published.gt(now - 1.days()))
628 .order_by(comment_aggregates::score.desc()),
631 let (limit, offset) = limit_and_offset(self.page, self.limit);
633 // Note: deleted and removed comments are done on the front side
637 .load::<CommentViewTuple>(self.conn)?;
639 Ok(CommentView::to_vec(res))
643 impl ViewToVec for CommentView {
644 type DbTuple = CommentViewTuple;
645 fn to_vec(posts: Vec<Self::DbTuple>) -> Vec<Self> {
649 comment: a.0.to_owned(),
650 creator: a.1.to_owned(),
651 recipient: a.3.to_owned(),
652 post: a.4.to_owned(),
653 community: a.5.to_owned(),
654 counts: a.6.to_owned(),
655 creator_banned_from_community: a.7.is_some(),
656 subscribed: a.8.is_some(),
657 saved: a.9.is_some(),
660 .collect::<Vec<Self>>()