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