]> Untitled Git - lemmy.git/blob - crates/api/src/site/search.rs
First pass at adding comment trees. (#2362)
[lemmy.git] / crates / api / src / site / search.rs
1 use crate::Perform;
2 use actix_web::web::Data;
3 use lemmy_api_common::{
4   site::{Search, SearchResponse},
5   utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
6 };
7 use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
8 use lemmy_db_schema::{
9   source::community::Community,
10   traits::DeleteableOrRemoveable,
11   utils::post_to_comment_sort_type,
12   SearchType,
13 };
14 use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
15 use lemmy_db_views_actor::{
16   community_view::CommunityQueryBuilder,
17   person_view::PersonQueryBuilder,
18 };
19 use lemmy_utils::{error::LemmyError, ConnectionId};
20 use lemmy_websocket::LemmyContext;
21
22 #[async_trait::async_trait(?Send)]
23 impl Perform for Search {
24   type Response = SearchResponse;
25
26   #[tracing::instrument(skip(context, _websocket_id))]
27   async fn perform(
28     &self,
29     context: &Data<LemmyContext>,
30     _websocket_id: Option<ConnectionId>,
31   ) -> Result<SearchResponse, LemmyError> {
32     let data: &Search = self;
33
34     let local_user_view =
35       get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
36         .await?;
37
38     check_private_instance(&local_user_view, context.pool()).await?;
39
40     let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
41     let show_bot_accounts = local_user_view
42       .as_ref()
43       .map(|t| t.local_user.show_bot_accounts);
44     let show_read_posts = local_user_view
45       .as_ref()
46       .map(|t| t.local_user.show_read_posts);
47
48     let person_id = local_user_view.map(|u| u.person.id);
49
50     let mut posts = Vec::new();
51     let mut comments = Vec::new();
52     let mut communities = Vec::new();
53     let mut users = Vec::new();
54
55     // TODO no clean / non-nsfw searching rn
56
57     let q = data.q.to_owned();
58     let page = data.page;
59     let limit = data.limit;
60     let sort = data.sort;
61     let listing_type = data.listing_type;
62     let search_type = data.type_.unwrap_or(SearchType::All);
63     let community_id = data.community_id;
64     let community_actor_id = if let Some(name) = &data.community_name {
65       resolve_actor_identifier::<ApubCommunity, Community>(name, context)
66         .await
67         .ok()
68         .map(|c| c.actor_id)
69     } else {
70       None
71     };
72     let creator_id = data.creator_id;
73     match search_type {
74       SearchType::Posts => {
75         posts = blocking(context.pool(), move |conn| {
76           PostQueryBuilder::create(conn)
77             .sort(sort)
78             .show_nsfw(show_nsfw)
79             .show_bot_accounts(show_bot_accounts)
80             .show_read_posts(show_read_posts)
81             .listing_type(listing_type)
82             .community_id(community_id)
83             .community_actor_id(community_actor_id)
84             .creator_id(creator_id)
85             .my_person_id(person_id)
86             .search_term(q)
87             .page(page)
88             .limit(limit)
89             .list()
90         })
91         .await??;
92       }
93       SearchType::Comments => {
94         comments = blocking(context.pool(), move |conn| {
95           CommentQueryBuilder::create(conn)
96             .sort(sort.map(post_to_comment_sort_type))
97             .listing_type(listing_type)
98             .search_term(q)
99             .show_bot_accounts(show_bot_accounts)
100             .community_id(community_id)
101             .community_actor_id(community_actor_id)
102             .creator_id(creator_id)
103             .my_person_id(person_id)
104             .page(page)
105             .limit(limit)
106             .list()
107         })
108         .await??;
109       }
110       SearchType::Communities => {
111         communities = blocking(context.pool(), move |conn| {
112           CommunityQueryBuilder::create(conn)
113             .sort(sort)
114             .listing_type(listing_type)
115             .search_term(q)
116             .my_person_id(person_id)
117             .page(page)
118             .limit(limit)
119             .list()
120         })
121         .await??;
122       }
123       SearchType::Users => {
124         users = blocking(context.pool(), move |conn| {
125           PersonQueryBuilder::create(conn)
126             .sort(sort)
127             .search_term(q)
128             .page(page)
129             .limit(limit)
130             .list()
131         })
132         .await??;
133       }
134       SearchType::All => {
135         // If the community or creator is included, dont search communities or users
136         let community_or_creator_included =
137           data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some();
138         let community_actor_id_2 = community_actor_id.to_owned();
139
140         posts = blocking(context.pool(), move |conn| {
141           PostQueryBuilder::create(conn)
142             .sort(sort)
143             .show_nsfw(show_nsfw)
144             .show_bot_accounts(show_bot_accounts)
145             .show_read_posts(show_read_posts)
146             .listing_type(listing_type)
147             .community_id(community_id)
148             .community_actor_id(community_actor_id_2)
149             .creator_id(creator_id)
150             .my_person_id(person_id)
151             .search_term(q)
152             .page(page)
153             .limit(limit)
154             .list()
155         })
156         .await??;
157
158         let q = data.q.to_owned();
159         let community_actor_id = community_actor_id.to_owned();
160
161         comments = blocking(context.pool(), move |conn| {
162           CommentQueryBuilder::create(conn)
163             .sort(sort.map(post_to_comment_sort_type))
164             .listing_type(listing_type)
165             .search_term(q)
166             .show_bot_accounts(show_bot_accounts)
167             .community_id(community_id)
168             .community_actor_id(community_actor_id)
169             .creator_id(creator_id)
170             .my_person_id(person_id)
171             .page(page)
172             .limit(limit)
173             .list()
174         })
175         .await??;
176
177         let q = data.q.to_owned();
178
179         communities = if community_or_creator_included {
180           vec![]
181         } else {
182           blocking(context.pool(), move |conn| {
183             CommunityQueryBuilder::create(conn)
184               .sort(sort)
185               .listing_type(listing_type)
186               .search_term(q)
187               .my_person_id(person_id)
188               .page(page)
189               .limit(limit)
190               .list()
191           })
192           .await??
193         };
194
195         let q = data.q.to_owned();
196
197         users = if community_or_creator_included {
198           vec![]
199         } else {
200           blocking(context.pool(), move |conn| {
201             PersonQueryBuilder::create(conn)
202               .sort(sort)
203               .search_term(q)
204               .page(page)
205               .limit(limit)
206               .list()
207           })
208           .await??
209         };
210       }
211       SearchType::Url => {
212         posts = blocking(context.pool(), move |conn| {
213           PostQueryBuilder::create(conn)
214             .sort(sort)
215             .show_nsfw(show_nsfw)
216             .show_bot_accounts(show_bot_accounts)
217             .show_read_posts(show_read_posts)
218             .listing_type(listing_type)
219             .my_person_id(person_id)
220             .community_id(community_id)
221             .community_actor_id(community_actor_id)
222             .creator_id(creator_id)
223             .url_search(q)
224             .page(page)
225             .limit(limit)
226             .list()
227         })
228         .await??;
229       }
230     };
231
232     // Blank out deleted or removed info for non logged in users
233     if person_id.is_none() {
234       for cv in communities
235         .iter_mut()
236         .filter(|cv| cv.community.deleted || cv.community.removed)
237       {
238         cv.community = cv.to_owned().community.blank_out_deleted_or_removed_info();
239       }
240
241       for pv in posts
242         .iter_mut()
243         .filter(|p| p.post.deleted || p.post.removed)
244       {
245         pv.post = pv.to_owned().post.blank_out_deleted_or_removed_info();
246       }
247
248       for cv in comments
249         .iter_mut()
250         .filter(|cv| cv.comment.deleted || cv.comment.removed)
251       {
252         cv.comment = cv.to_owned().comment.blank_out_deleted_or_removed_info();
253       }
254     }
255
256     // Return the jwt
257     Ok(SearchResponse {
258       type_: search_type.to_string(),
259       comments,
260       posts,
261       communities,
262       users,
263     })
264   }
265 }