1use chrono::{Datelike, Months, NaiveDate, NaiveDateTime, Utc};
4use diesel::dsl::case_when;
5use diesel::dsl::{exists, sql};
6use diesel::pg::Pg;
7use diesel::sql_types::{Float, Integer};
8use diesel::{
9 debug_query, select, BoolExpressionMethods, ExpressionMethods, PgTextExpressionMethods,
10 QueryDsl, QueryResult,
11};
12use diesel_async::{AsyncPgConnection, RunQueryDsl};
13use log::debug;
14use uuid::Uuid;
15
16use crate::config::auth::user_info::UserInfo;
17use crate::db::function::{similarity, PgTrgmExpressionMethods};
18use crate::db::pagination::Paginate;
19use crate::model::dto::{
20 MapSearchParameters, Page, PageParameters, UpdateMapDto, UpdateMapGeometryDto,
21};
22use crate::model::entity::{UpdateMap, UpdateMapGeometry};
23use crate::{
24 model::dto::{MapDto, NewMapDto},
25 schema::maps::{self, all_columns, deletion_date, name},
26};
27
28use super::{Map, NewMap};
29
30impl Map {
31 #[cfg(not(feature = "access_control"))]
32 pub async fn find(
47 search_parameters: MapSearchParameters,
48 page_parameters: PageParameters,
49 conn: &mut AsyncPgConnection,
50 _user_info: UserInfo,
51 _collaborating_in: Vec<i32>,
52 ) -> QueryResult<Page<MapDto>> {
53 let mut query = maps::table
54 .select((
55 similarity(name, search_parameters.name.clone().unwrap_or_default()),
56 all_columns,
57 ))
58 .into_boxed();
59
60 if let Some(search_query) = &search_parameters.name {
61 if !search_query.is_empty() {
62 query = query.filter(
63 name.fuzzy(search_query)
64 .or(name.ilike(format!("%{search_query}%"))),
65 );
66 }
67 }
68
69 let priority_order = case_when(maps::created_by.eq(&user_info.id), 0.into_sql::<Integer>())
71 .when(maps::id.eq_any(&collaborating_in), 1.into_sql::<Integer>())
72 .otherwise(2.into_sql::<Integer>());
73
74 let query = query
75 .filter(deletion_date.is_null())
76 .order_by(priority_order.asc())
77 .then_order_by(sql::<Float>("1").desc())
78 .then_order_by(map::modified_at.desc())
79 .paginate(page_parameters.page)
80 .per_page(page_parameters.per_page);
81 debug!("{}", debug_query::<Pg, _>(&query));
82 query
83 .load_page::<(f32, Self)>(conn)
84 .await
85 .map(Page::from_entity)
86 }
87
88 #[cfg(feature = "access_control")]
89 pub async fn find(
104 search_parameters: MapSearchParameters,
105 page_parameters: PageParameters,
106 conn: &mut AsyncPgConnection,
107 user_info: UserInfo,
108 collaborating_in: Vec<i64>,
109 ) -> QueryResult<Page<MapDto>> {
110 use diesel::IntoSql;
111
112 use crate::model::r#enum::privacy_access_control::PrivacyAccessControl;
113 use crate::schema::maps::dsl as map;
114 let mut query = maps::table
115 .select((
116 similarity(name, search_parameters.name.clone().unwrap_or_default()),
117 all_columns,
118 ))
119 .into_boxed();
120
121 if let Some(search_query) = &search_parameters.name {
122 if !search_query.is_empty() {
123 query = query.filter(
124 name.fuzzy(search_query)
125 .or(name.ilike(format!("%{search_query}%"))),
126 );
127 }
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(PrivacyAccessControl::Public)),
136 );
137 if user_info.is_member() {
138 query = query.or_filter(map::privacy.eq(PrivacyAccessControl::Protected));
139 }
140 }
141
142 let priority_order = case_when(maps::created_by.eq(&user_info.id), 0.into_sql::<Integer>())
144 .when(maps::id.eq_any(&collaborating_in), 1.into_sql::<Integer>())
145 .otherwise(2.into_sql::<Integer>());
146
147 let query = query
148 .filter(deletion_date.is_null())
149 .order_by(priority_order.asc())
150 .then_order_by(sql::<Float>("1").desc())
151 .then_order_by(map::modified_at.desc())
152 .paginate(page_parameters.page)
153 .per_page(page_parameters.per_page);
154 debug!("{}", debug_query::<Pg, _>(&query));
155 query
156 .load_page::<(f32, Self)>(conn)
157 .await
158 .map(Page::from_entity)
159 }
160
161 pub async fn find_by_id(id: i64, conn: &mut AsyncPgConnection) -> QueryResult<MapDto> {
166 let query = maps::table.find(id).filter(deletion_date.is_null());
167 debug!("{}", debug_query::<Pg, _>(&query));
168 query.first::<Self>(conn).await.map(Into::into)
169 }
170
171 pub async fn is_name_taken(map_name: &str, conn: &mut AsyncPgConnection) -> QueryResult<bool> {
176 let query = select(exists(maps::table.filter(name.eq(map_name))));
177 debug!("{}", debug_query::<Pg, _>(&query));
178 query.get_result(conn).await
179 }
180
181 pub async fn create(
186 new_map: NewMapDto,
187 user_id: Uuid,
188 conn: &mut AsyncPgConnection,
189 ) -> QueryResult<MapDto> {
190 let new_map = NewMap::from((new_map, user_id));
191 let query = diesel::insert_into(maps::table).values(&new_map);
192 debug!("{}", debug_query::<Pg, _>(&query));
193 query.get_result::<Self>(conn).await.map(Into::into)
194 }
195
196 pub async fn update(
201 map_update: UpdateMapDto,
202 id: i64,
203 conn: &mut AsyncPgConnection,
204 ) -> QueryResult<MapDto> {
205 let map_update = UpdateMap::from(map_update);
206 let query = diesel::update(maps::table.find(id)).set(&map_update);
207 debug!("{}", debug_query::<Pg, _>(&query));
208 query.get_result::<Self>(conn).await.map(Into::into)
209 }
210
211 pub async fn mark_for_deletion(id: i64, conn: &mut AsyncPgConnection) -> QueryResult<MapDto> {
216 let now = Utc::now().naive_utc();
217 let in_one_month = now + Months::new(1);
218 let in_one_month_as_naive_date = NaiveDate::from_ymd_opt(
220 in_one_month.year(),
221 in_one_month.month(),
222 in_one_month.day(),
223 );
224
225 let map = Self::find_by_id(id, conn).await?;
226
227 let query = diesel::update(maps::table.find(id)).set((
228 deletion_date.eq(in_one_month_as_naive_date),
229 name.eq(format!("{} __DELETED {}", map.name, now)),
231 ));
232 debug!("{}", debug_query::<Pg, _>(&query));
233 query.get_result::<Self>(conn).await.map(Into::into)
234 }
235
236 pub async fn update_geometry(
241 map_update_bounds: UpdateMapGeometryDto,
242 id: i64,
243 conn: &mut AsyncPgConnection,
244 ) -> QueryResult<MapDto> {
245 let map_update = UpdateMapGeometry::from(map_update_bounds);
246 let query = diesel::update(maps::table.find(id)).set(&map_update);
247 debug!("{}", debug_query::<Pg, _>(&query));
248 query.get_result::<Self>(conn).await.map(Into::into)
249 }
250
251 pub async fn update_modified_metadata(
256 id: i64,
257 user_id: Uuid,
258 time: NaiveDateTime,
259 conn: &mut AsyncPgConnection,
260 ) -> QueryResult<()> {
261 diesel::update(maps::table.find(id))
262 .set((maps::modified_at.eq(time), maps::modified_by.eq(user_id)))
263 .execute(conn)
264 .await?;
265 Ok(())
266 }
267}