]> Untitled Git - lemmy.git/blob - crates/db_views_actor/src/comment_reply_view.rs
Reduce amount of columns selected (#3755)
[lemmy.git] / crates / db_views_actor / src / comment_reply_view.rs
1 use crate::structs::CommentReplyView;
2 use diesel::{
3   pg::Pg,
4   result::Error,
5   BoolExpressionMethods,
6   ExpressionMethods,
7   JoinOnDsl,
8   NullableExpressionMethods,
9   QueryDsl,
10 };
11 use diesel_async::RunQueryDsl;
12 use lemmy_db_schema::{
13   aggregates::structs::CommentAggregates,
14   aliases,
15   newtypes::{CommentReplyId, PersonId},
16   schema::{
17     comment,
18     comment_aggregates,
19     comment_like,
20     comment_reply,
21     comment_saved,
22     community,
23     community_follower,
24     community_person_ban,
25     person,
26     person_block,
27     post,
28   },
29   source::{
30     comment::Comment,
31     comment_reply::CommentReply,
32     community::{Community, CommunityFollower},
33     person::Person,
34     post::Post,
35   },
36   traits::JoinView,
37   utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
38   CommentSortType,
39   SubscribedType,
40 };
41
42 type CommentReplyViewTuple = (
43   CommentReply,
44   Comment,
45   Person,
46   Post,
47   Community,
48   Person,
49   CommentAggregates,
50   bool,
51   SubscribedType,
52   bool,
53   bool,
54   Option<i16>,
55 );
56
57 fn queries<'a>() -> Queries<
58   impl ReadFn<'a, CommentReplyView, (CommentReplyId, Option<PersonId>)>,
59   impl ListFn<'a, CommentReplyView, CommentReplyQuery>,
60 > {
61   let all_joins = |query: comment_reply::BoxedQuery<'a, Pg>, my_person_id: Option<PersonId>| {
62     // The left join below will return None in this case
63     let person_id_join = my_person_id.unwrap_or(PersonId(-1));
64
65     query
66       .inner_join(comment::table)
67       .inner_join(person::table.on(comment::creator_id.eq(person::id)))
68       .inner_join(post::table.on(comment::post_id.eq(post::id)))
69       .inner_join(community::table.on(post::community_id.eq(community::id)))
70       .inner_join(aliases::person1)
71       .inner_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
72       .left_join(
73         community_person_ban::table.on(
74           community::id
75             .eq(community_person_ban::community_id)
76             .and(community_person_ban::person_id.eq(comment::creator_id)),
77         ),
78       )
79       .left_join(
80         community_follower::table.on(
81           post::community_id
82             .eq(community_follower::community_id)
83             .and(community_follower::person_id.eq(person_id_join)),
84         ),
85       )
86       .left_join(
87         comment_saved::table.on(
88           comment::id
89             .eq(comment_saved::comment_id)
90             .and(comment_saved::person_id.eq(person_id_join)),
91         ),
92       )
93       .left_join(
94         person_block::table.on(
95           comment::creator_id
96             .eq(person_block::target_id)
97             .and(person_block::person_id.eq(person_id_join)),
98         ),
99       )
100       .left_join(
101         comment_like::table.on(
102           comment::id
103             .eq(comment_like::comment_id)
104             .and(comment_like::person_id.eq(person_id_join)),
105         ),
106       )
107       .select((
108         comment_reply::all_columns,
109         comment::all_columns,
110         person::all_columns,
111         post::all_columns,
112         community::all_columns,
113         aliases::person1.fields(person::all_columns),
114         comment_aggregates::all_columns,
115         community_person_ban::id.nullable().is_not_null(),
116         CommunityFollower::select_subscribed_type(),
117         comment_saved::id.nullable().is_not_null(),
118         person_block::id.nullable().is_not_null(),
119         comment_like::score.nullable(),
120       ))
121   };
122
123   let read =
124     move |mut conn: DbConn<'a>,
125           (comment_reply_id, my_person_id): (CommentReplyId, Option<PersonId>)| async move {
126       all_joins(
127         comment_reply::table.find(comment_reply_id).into_boxed(),
128         my_person_id,
129       )
130       .first::<CommentReplyViewTuple>(&mut conn)
131       .await
132     };
133
134   let list = move |mut conn: DbConn<'a>, options: CommentReplyQuery| async move {
135     let mut query = all_joins(comment_reply::table.into_boxed(), options.my_person_id);
136
137     if let Some(recipient_id) = options.recipient_id {
138       query = query.filter(comment_reply::recipient_id.eq(recipient_id));
139     }
140
141     if options.unread_only.unwrap_or(false) {
142       query = query.filter(comment_reply::read.eq(false));
143     }
144
145     if !options.show_bot_accounts.unwrap_or(true) {
146       query = query.filter(person::bot_account.eq(false));
147     };
148
149     query = match options.sort.unwrap_or(CommentSortType::New) {
150       CommentSortType::Hot => query.then_order_by(comment_aggregates::hot_rank.desc()),
151       CommentSortType::Controversial => {
152         query.then_order_by(comment_aggregates::controversy_rank.desc())
153       }
154       CommentSortType::New => query.then_order_by(comment_reply::published.desc()),
155       CommentSortType::Old => query.then_order_by(comment_reply::published.asc()),
156       CommentSortType::Top => query.order_by(comment_aggregates::score.desc()),
157     };
158
159     let (limit, offset) = limit_and_offset(options.page, options.limit)?;
160
161     query
162       .limit(limit)
163       .offset(offset)
164       .load::<CommentReplyViewTuple>(&mut conn)
165       .await
166   };
167
168   Queries::new(read, list)
169 }
170
171 impl CommentReplyView {
172   pub async fn read(
173     pool: &mut DbPool<'_>,
174     comment_reply_id: CommentReplyId,
175     my_person_id: Option<PersonId>,
176   ) -> Result<Self, Error> {
177     queries().read(pool, (comment_reply_id, my_person_id)).await
178   }
179
180   /// Gets the number of unread replies
181   pub async fn get_unread_replies(
182     pool: &mut DbPool<'_>,
183     my_person_id: PersonId,
184   ) -> Result<i64, Error> {
185     use diesel::dsl::count;
186
187     let conn = &mut get_conn(pool).await?;
188
189     comment_reply::table
190       .inner_join(comment::table)
191       .filter(comment_reply::recipient_id.eq(my_person_id))
192       .filter(comment_reply::read.eq(false))
193       .filter(comment::deleted.eq(false))
194       .filter(comment::removed.eq(false))
195       .select(count(comment_reply::id))
196       .first::<i64>(conn)
197       .await
198   }
199 }
200
201 #[derive(Default)]
202 pub struct CommentReplyQuery {
203   pub my_person_id: Option<PersonId>,
204   pub recipient_id: Option<PersonId>,
205   pub sort: Option<CommentSortType>,
206   pub unread_only: Option<bool>,
207   pub show_bot_accounts: Option<bool>,
208   pub page: Option<i64>,
209   pub limit: Option<i64>,
210 }
211
212 impl CommentReplyQuery {
213   pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<CommentReplyView>, Error> {
214     queries().list(pool, self).await
215   }
216 }
217
218 impl JoinView for CommentReplyView {
219   type JoinTuple = CommentReplyViewTuple;
220   fn from_tuple(a: Self::JoinTuple) -> Self {
221     Self {
222       comment_reply: a.0,
223       comment: a.1,
224       creator: a.2,
225       post: a.3,
226       community: a.4,
227       recipient: a.5,
228       counts: a.6,
229       creator_banned_from_community: a.7,
230       subscribed: a.8,
231       saved: a.9,
232       creator_blocked: a.10,
233       my_vote: a.11,
234     }
235   }
236 }