113 lines
3.7 KiB
Rust
113 lines
3.7 KiB
Rust
use libertas_core::{
|
|
error::{CoreError, CoreResult},
|
|
schema::{ListMediaOptions, SortOrder},
|
|
};
|
|
use sqlx::QueryBuilder as SqlxQueryBuilder;
|
|
|
|
pub trait QueryBuilder<T> {
|
|
fn apply_options_to_query<'a>(
|
|
&self,
|
|
query: SqlxQueryBuilder<'a, sqlx::Postgres>,
|
|
options: &'a T,
|
|
) -> CoreResult<SqlxQueryBuilder<'a, sqlx::Postgres>>;
|
|
}
|
|
|
|
pub struct MediaQueryBuilder {
|
|
allowed_sort_columns: Vec<String>,
|
|
}
|
|
|
|
impl MediaQueryBuilder {
|
|
pub fn new(allowed_sort_columns: Vec<String>) -> Self {
|
|
Self {
|
|
allowed_sort_columns,
|
|
}
|
|
}
|
|
|
|
fn validate_sort_column<'a>(&self, column: &'a str) -> CoreResult<&'a str> {
|
|
if self.allowed_sort_columns.contains(&column.to_string()) {
|
|
Ok(column)
|
|
} else {
|
|
Err(CoreError::Validation(format!(
|
|
"Sorting by '{}' is not supported",
|
|
column
|
|
)))
|
|
}
|
|
}
|
|
|
|
pub fn apply_filters_to_query<'a>(
|
|
&self,
|
|
mut query: SqlxQueryBuilder<'a, sqlx::Postgres>,
|
|
options: &'a ListMediaOptions,
|
|
) -> CoreResult<(SqlxQueryBuilder<'a, sqlx::Postgres>, i64)> {
|
|
let mut metadata_filter_count = 0;
|
|
if let Some(filter) = &options.filter {
|
|
if let Some(mime) = &filter.mime_type {
|
|
query.push(" AND media.mime_type = ");
|
|
query.push_bind(mime);
|
|
}
|
|
|
|
if let Some(metadata_filters) = &filter.metadata_filters {
|
|
if !metadata_filters.is_empty() {
|
|
metadata_filter_count = metadata_filters.len() as i64;
|
|
query.push(" JOIN media_metadata mm ON media.id = mm.media_id ");
|
|
query.push(" AND ( ");
|
|
|
|
for (i, filter) in metadata_filters.iter().enumerate() {
|
|
if i > 0 {
|
|
query.push(" OR ");
|
|
}
|
|
query.push(" ( mm.tag_name = ");
|
|
query.push_bind(&filter.tag_name);
|
|
query.push(" AND mm.tag_value = ");
|
|
query.push_bind(&filter.tag_value);
|
|
query.push(" ) ");
|
|
}
|
|
query.push(" ) ");
|
|
}
|
|
}
|
|
}
|
|
Ok((query, metadata_filter_count))
|
|
}
|
|
|
|
pub fn apply_sorting_to_query<'a>(
|
|
&self,
|
|
mut query: SqlxQueryBuilder<'a, sqlx::Postgres>,
|
|
options: &'a ListMediaOptions,
|
|
) -> CoreResult<SqlxQueryBuilder<'a, sqlx::Postgres>> {
|
|
if let Some(sort) = &options.sort {
|
|
let column = self.validate_sort_column(&sort.sort_by)?;
|
|
let direction = match sort.sort_order {
|
|
SortOrder::Asc => "ASC",
|
|
SortOrder::Desc => "DESC",
|
|
};
|
|
let nulls_order = if direction == "ASC" {
|
|
"NULLS LAST"
|
|
} else {
|
|
"NULLS FIRST"
|
|
};
|
|
let order_by_clause = format!("ORDER BY {} {} {}", column, direction, nulls_order);
|
|
query.push(order_by_clause);
|
|
} else {
|
|
query.push(" ORDER BY media.created_at DESC NULLS LAST ");
|
|
}
|
|
Ok(query)
|
|
}
|
|
|
|
pub fn apply_pagination_to_query<'a>(
|
|
&self,
|
|
mut query: SqlxQueryBuilder<'a, sqlx::Postgres>,
|
|
options: &'a ListMediaOptions,
|
|
) -> CoreResult<SqlxQueryBuilder<'a, sqlx::Postgres>> {
|
|
if let Some(pagination) = &options.pagination {
|
|
let limit = pagination.limit as i64;
|
|
let offset = (pagination.page.saturating_sub(1) as i64) * limit;
|
|
|
|
query.push(" LIMIT ");
|
|
query.push_bind(limit);
|
|
query.push(" OFFSET ");
|
|
query.push_bind(offset);
|
|
}
|
|
Ok(query)
|
|
}
|
|
}
|