use chrono::{Datelike, Months, NaiveDate, NaiveDateTime, Utc};
use diesel::dsl::{exists, sql};
use diesel::pg::Pg;
use diesel::sql_types::Float;
use diesel::{
debug_query, select, BoolExpressionMethods, ExpressionMethods, PgTextExpressionMethods,
QueryDsl, QueryResult,
};
use diesel_async::{AsyncPgConnection, RunQueryDsl};
use log::debug;
use uuid::Uuid;
use crate::db::function::{similarity, PgTrgmExpressionMethods};
use crate::db::pagination::Paginate;
use crate::model::dto::{
MapSearchParameters, Page, PageParameters, UpdateMapDto, UpdateMapGeometryDto,
};
use crate::model::entity::{UpdateMap, UpdateMapGeometry};
use crate::{
model::dto::{MapDto, NewMapDto},
schema::maps::{self, all_columns, created_by, deletion_date, is_inactive, name, privacy},
};
use super::{Map, NewMap};
impl Map {
pub async fn find(
search_parameters: MapSearchParameters,
page_parameters: PageParameters,
conn: &mut AsyncPgConnection,
) -> QueryResult<Page<MapDto>> {
let mut query = maps::table
.select((
similarity(name, search_parameters.name.clone().unwrap_or_default()),
all_columns,
))
.into_boxed();
if let Some(search_query) = &search_parameters.name {
if !search_query.is_empty() {
query = query.filter(
name.fuzzy(search_query)
.or(name.ilike(format!("%{search_query}%"))),
);
}
}
if let Some(is_inactive_search) = search_parameters.is_inactive {
query = query.filter(is_inactive.eq(is_inactive_search));
}
if let Some(privacy_search) = search_parameters.privacy {
query = query.filter(privacy.eq(privacy_search));
}
if let Some(created_by_search) = search_parameters.created_by {
query = query.filter(created_by.eq(created_by_search));
}
let query = query
.filter(deletion_date.is_null())
.order(sql::<Float>("1").desc())
.paginate(page_parameters.page)
.per_page(page_parameters.per_page);
debug!("{}", debug_query::<Pg, _>(&query));
query
.load_page::<(f32, Self)>(conn)
.await
.map(Page::from_entity)
}
pub async fn find_by_id(id: i32, conn: &mut AsyncPgConnection) -> QueryResult<MapDto> {
let query = maps::table.find(id).filter(deletion_date.is_null());
debug!("{}", debug_query::<Pg, _>(&query));
query.first::<Self>(conn).await.map(Into::into)
}
pub async fn is_name_taken(map_name: &str, conn: &mut AsyncPgConnection) -> QueryResult<bool> {
let query = select(exists(maps::table.filter(name.eq(map_name))));
debug!("{}", debug_query::<Pg, _>(&query));
query.get_result(conn).await
}
pub async fn create(
new_map: NewMapDto,
user_id: Uuid,
conn: &mut AsyncPgConnection,
) -> QueryResult<MapDto> {
let new_map = NewMap::from((new_map, user_id));
let query = diesel::insert_into(maps::table).values(&new_map);
debug!("{}", debug_query::<Pg, _>(&query));
query.get_result::<Self>(conn).await.map(Into::into)
}
pub async fn update(
map_update: UpdateMapDto,
id: i32,
conn: &mut AsyncPgConnection,
) -> QueryResult<MapDto> {
let map_update = UpdateMap::from(map_update);
let query = diesel::update(maps::table.find(id)).set(&map_update);
debug!("{}", debug_query::<Pg, _>(&query));
query.get_result::<Self>(conn).await.map(Into::into)
}
pub async fn mark_for_deletion(id: i32, conn: &mut AsyncPgConnection) -> QueryResult<MapDto> {
let now = Utc::now().naive_utc();
let in_one_month = now + Months::new(1);
let in_one_month_as_naive_date = NaiveDate::from_ymd_opt(
in_one_month.year(),
in_one_month.month(),
in_one_month.day(),
);
let map = Self::find_by_id(id, conn).await?;
let query = diesel::update(maps::table.find(id)).set((
deletion_date.eq(in_one_month_as_naive_date),
name.eq(format!("{} __DELETED {}", map.name, now)),
));
debug!("{}", debug_query::<Pg, _>(&query));
query.get_result::<Self>(conn).await.map(Into::into)
}
pub async fn update_geometry(
map_update_bounds: UpdateMapGeometryDto,
id: i32,
conn: &mut AsyncPgConnection,
) -> QueryResult<MapDto> {
let map_update = UpdateMapGeometry::from(map_update_bounds);
let query = diesel::update(maps::table.find(id)).set(&map_update);
debug!("{}", debug_query::<Pg, _>(&query));
query.get_result::<Self>(conn).await.map(Into::into)
}
pub async fn update_modified_metadata(
id: i32,
user_id: Uuid,
time: NaiveDateTime,
conn: &mut AsyncPgConnection,
) -> QueryResult<()> {
diesel::update(maps::table.find(id))
.set((maps::modified_at.eq(time), maps::modified_by.eq(user_id)))
.execute(conn)
.await?;
Ok(())
}
}