1use chrono::{Datelike, Months, NaiveDate, NaiveDateTime, Utc};
4use diesel::dsl::{exists, sql};
5use diesel::pg::Pg;
6use diesel::sql_types::Float;
7use diesel::{
8 debug_query, select, BoolExpressionMethods, ExpressionMethods, PgTextExpressionMethods,
9 QueryDsl, QueryResult,
10};
11use diesel_async::{AsyncPgConnection, RunQueryDsl};
12use log::debug;
13use uuid::Uuid;
14
15use crate::config::auth::user_info::UserInfo;
16use crate::db::function::{similarity, PgTrgmExpressionMethods};
17use crate::db::pagination::Paginate;
18use crate::model::dto::{
19 MapSearchParameters, Page, PageParameters, UpdateMapDto, UpdateMapGeometryDto,
20};
21use crate::model::entity::{UpdateMap, UpdateMapGeometry};
22use crate::{
23 model::dto::{MapDto, NewMapDto},
24 schema::maps::{self, all_columns, created_by, deletion_date, is_inactive, name, privacy},
25};
26
27use super::{Map, NewMap};
28
29impl Map {
30 #[cfg(not(feature = "access_control"))]
31 pub async fn find(
42 search_parameters: MapSearchParameters,
43 page_parameters: PageParameters,
44 conn: &mut AsyncPgConnection,
45 _user_info: UserInfo,
46 _collaborating_in: Vec<i32>,
47 ) -> QueryResult<Page<MapDto>> {
48 let mut query = maps::table
49 .select((
50 similarity(name, search_parameters.name.clone().unwrap_or_default()),
51 all_columns,
52 ))
53 .into_boxed();
54
55 if let Some(search_query) = &search_parameters.name {
56 if !search_query.is_empty() {
57 query = query.filter(
58 name.fuzzy(search_query)
59 .or(name.ilike(format!("%{search_query}%"))),
60 );
61 }
62 }
63 if let Some(is_inactive_search) = search_parameters.is_inactive {
64 query = query.filter(is_inactive.eq(is_inactive_search));
65 }
66 if let Some(privacy_search) = search_parameters.privacy {
67 query = query.filter(privacy.eq(privacy_search));
68 }
69 if let Some(created_by_search) = search_parameters.created_by {
70 query = query.filter(created_by.eq(created_by_search));
71 }
72
73 let query = query
74 .filter(deletion_date.is_null())
75 .order(sql::<Float>("1").desc())
76 .paginate(page_parameters.page)
77 .per_page(page_parameters.per_page);
78 debug!("{}", debug_query::<Pg, _>(&query));
79 query
80 .load_page::<(f32, Self)>(conn)
81 .await
82 .map(Page::from_entity)
83 }
84
85 #[cfg(feature = "access_control")]
86 pub async fn find(
97 search_parameters: MapSearchParameters,
98 page_parameters: PageParameters,
99 conn: &mut AsyncPgConnection,
100 user_info: UserInfo,
101 collaborating_in: Vec<i32>,
102 ) -> QueryResult<Page<MapDto>> {
103 use crate::model::r#enum::privacy_option::PrivacyOption;
104 use crate::schema::maps::dsl as map;
105 let mut query = maps::table
106 .select((
107 similarity(name, search_parameters.name.clone().unwrap_or_default()),
108 all_columns,
109 ))
110 .into_boxed();
111
112 if let Some(search_query) = &search_parameters.name {
113 if !search_query.is_empty() {
114 query = query.filter(
115 name.fuzzy(search_query)
116 .or(name.ilike(format!("%{search_query}%"))),
117 );
118 }
119 }
120 if let Some(is_inactive_search) = search_parameters.is_inactive {
121 query = query.filter(is_inactive.eq(is_inactive_search));
122 }
123 if let Some(privacy_search) = search_parameters.privacy {
124 query = query.filter(privacy.eq(privacy_search));
125 }
126 if let Some(created_by_search) = search_parameters.created_by {
127 query = query.filter(created_by.eq(created_by_search));
128 }
129
130 if !user_info.is_admin() {
131 query = query.filter(
132 map::created_by
133 .eq(user_info.id)
134 .or(map::id.eq_any(collaborating_in.clone()))
135 .or(map::privacy.eq(PrivacyOption::Public)),
136 );
137 if user_info.is_member() {
138 query = query.or_filter(map::privacy.eq(PrivacyOption::Protected));
139 }
140 }
141
142 let query = query
143 .filter(deletion_date.is_null())
144 .order(sql::<Float>("1").desc())
145 .paginate(page_parameters.page)
146 .per_page(page_parameters.per_page);
147 debug!("{}", debug_query::<Pg, _>(&query));
148 query
149 .load_page::<(f32, Self)>(conn)
150 .await
151 .map(Page::from_entity)
152 }
153
154 pub async fn find_by_id(id: i32, conn: &mut AsyncPgConnection) -> QueryResult<MapDto> {
159 let query = maps::table.find(id).filter(deletion_date.is_null());
160 debug!("{}", debug_query::<Pg, _>(&query));
161 query.first::<Self>(conn).await.map(Into::into)
162 }
163
164 pub async fn is_name_taken(map_name: &str, conn: &mut AsyncPgConnection) -> QueryResult<bool> {
169 let query = select(exists(maps::table.filter(name.eq(map_name))));
170 debug!("{}", debug_query::<Pg, _>(&query));
171 query.get_result(conn).await
172 }
173
174 pub async fn create(
179 new_map: NewMapDto,
180 user_id: Uuid,
181 conn: &mut AsyncPgConnection,
182 ) -> QueryResult<MapDto> {
183 let new_map = NewMap::from((new_map, user_id));
184 let query = diesel::insert_into(maps::table).values(&new_map);
185 debug!("{}", debug_query::<Pg, _>(&query));
186 query.get_result::<Self>(conn).await.map(Into::into)
187 }
188
189 pub async fn update(
194 map_update: UpdateMapDto,
195 id: i32,
196 conn: &mut AsyncPgConnection,
197 ) -> QueryResult<MapDto> {
198 let map_update = UpdateMap::from(map_update);
199 let query = diesel::update(maps::table.find(id)).set(&map_update);
200 debug!("{}", debug_query::<Pg, _>(&query));
201 query.get_result::<Self>(conn).await.map(Into::into)
202 }
203
204 pub async fn mark_for_deletion(id: i32, conn: &mut AsyncPgConnection) -> QueryResult<MapDto> {
209 let now = Utc::now().naive_utc();
210 let in_one_month = now + Months::new(1);
211 let in_one_month_as_naive_date = NaiveDate::from_ymd_opt(
213 in_one_month.year(),
214 in_one_month.month(),
215 in_one_month.day(),
216 );
217
218 let map = Self::find_by_id(id, conn).await?;
219
220 let query = diesel::update(maps::table.find(id)).set((
221 deletion_date.eq(in_one_month_as_naive_date),
222 name.eq(format!("{} __DELETED {}", map.name, now)),
224 ));
225 debug!("{}", debug_query::<Pg, _>(&query));
226 query.get_result::<Self>(conn).await.map(Into::into)
227 }
228
229 pub async fn update_geometry(
234 map_update_bounds: UpdateMapGeometryDto,
235 id: i32,
236 conn: &mut AsyncPgConnection,
237 ) -> QueryResult<MapDto> {
238 let map_update = UpdateMapGeometry::from(map_update_bounds);
239 let query = diesel::update(maps::table.find(id)).set(&map_update);
240 debug!("{}", debug_query::<Pg, _>(&query));
241 query.get_result::<Self>(conn).await.map(Into::into)
242 }
243
244 pub async fn update_modified_metadata(
249 id: i32,
250 user_id: Uuid,
251 time: NaiveDateTime,
252 conn: &mut AsyncPgConnection,
253 ) -> QueryResult<()> {
254 diesel::update(maps::table.find(id))
255 .set((maps::modified_at.eq(time), maps::modified_by.eq(user_id)))
256 .execute(conn)
257 .await?;
258 Ok(())
259 }
260}