feat: Implement advanced filtering with new filter conditions and a strategy-based query builder.

This commit is contained in:
2025-12-03 23:39:47 +01:00
parent 15177f218b
commit 333c180b17
9 changed files with 481 additions and 74 deletions

View File

@@ -5,7 +5,8 @@ use axum::{
use libertas_core::{
error::CoreError,
schema::{
FilterParams, ListMediaOptions, MetadataFilter, PaginationParams, SortOrder, SortParams,
FilterCondition, FilterOperator, FilterParams, ListMediaOptions, MetadataFilter,
PaginationParams, SortOrder, SortParams,
},
};
@@ -61,9 +62,44 @@ impl From<ListMediaParams> for ListMediaOptions {
Some(PaginationParams { page, limit })
};
let conditions = if params.filters.is_empty() {
None
} else {
let mut conds = Vec::new();
for filter_str in params.filters {
let parts: Vec<&str> = filter_str.splitn(3, ':').collect();
if parts.len() == 3 {
let field = parts[0].to_string();
let op_str = parts[1];
let value = parts[2].to_string();
let operator = match op_str.to_lowercase().as_str() {
"eq" => Some(FilterOperator::Eq),
"neq" => Some(FilterOperator::Neq),
"like" => Some(FilterOperator::Like),
"gt" => Some(FilterOperator::Gt),
"lt" => Some(FilterOperator::Lt),
"gte" => Some(FilterOperator::Gte),
"lte" => Some(FilterOperator::Lte),
_ => None,
};
if let Some(op) = operator {
conds.push(FilterCondition {
field,
operator: op,
value,
});
}
}
}
if conds.is_empty() { None } else { Some(conds) }
};
let filter = Some(FilterParams {
mime_type: params.mime_type,
metadata_filters,
conditions,
});
ListMediaOptions {
@@ -81,10 +117,33 @@ impl FromRequestParts<AppState> for ApiListMediaOptions {
parts: &mut Parts,
state: &AppState,
) -> Result<Self, Self::Rejection> {
let Query(params) = Query::<ListMediaParams>::from_request_parts(parts, state)
let Query(raw_params) = Query::<Vec<(String, String)>>::from_request_parts(parts, state)
.await
.map_err(|e| ApiError::from(CoreError::Validation(e.to_string())))?;
let mut params = ListMediaParams {
sort_by: None,
order: None,
mime_type: None,
metadata: Vec::new(),
filters: Vec::new(),
page: None,
limit: None,
};
for (key, value) in raw_params {
match key.as_str() {
"sort_by" => params.sort_by = Some(value),
"order" => params.order = Some(value),
"mime_type" => params.mime_type = Some(value),
"metadata" => params.metadata.push(value),
"filters" => params.filters.push(value),
"page" => params.page = value.parse().ok(),
"limit" => params.limit = value.parse().ok(),
_ => {}
}
}
Ok(ApiListMediaOptions(params.into()))
}
}

View File

@@ -42,6 +42,8 @@ pub struct ListMediaParams {
pub mime_type: Option<String>,
#[serde(default)]
pub metadata: Vec<String>,
#[serde(default)]
pub filters: Vec<String>,
pub page: Option<u32>,
pub limit: Option<u32>,
}