diff options
| -rw-r--r-- | src/models/model.rs | 52 | ||||
| -rw-r--r-- | src/routes/model.rs | 16 |
2 files changed, 67 insertions, 1 deletions
diff --git a/src/models/model.rs b/src/models/model.rs index 47864d9..30ccc84 100644 --- a/src/models/model.rs +++ b/src/models/model.rs @@ -39,6 +39,13 @@ pub struct ModelCreate { pub material: Option<String>, } +/// Payload used for model searching +#[derive(Deserialize)] +pub struct ModelFilter { + /// Stands for "query" + pub q: String, +} + #[derive(Serialize, sqlx::FromRow)] pub struct ModelUser { pub id: i32, @@ -203,6 +210,34 @@ impl Model { Ok(rows) } + /// Filter models by some cols + pub async fn filter(page: i64, query: String) -> Result<Vec<ModelUser>, AppError> { + let pool = unsafe { get_client() }; + let rows: Vec<ModelUser> = sqlx::query_as( + r#" + SELECT + models.*, + json_build_object('id', users.id, 'name', users.name, 'email', users.email, 'username', users.username, 'is_staff', users.is_staff, 'avatar', users.avatar) as author, + json_agg(uploads.*) filter (where uploads.* is not null) as uploads, + json_agg(likes.*) filter (where likes.* is not null) as likes + FROM models + JOIN users ON users.id = models.author_id + LEFT JOIN uploads ON uploads.model_id = models.id + LEFT JOIN likes ON likes.model_id = models.id + WHERE models.name ILIKE $1 OR description ILIKE $1 OR printer ILIKE $1 OR material ILIKE $1 + GROUP BY models.id, users.id + ORDER BY id DESC + LIMIT $2 OFFSET $3 + "#) + .bind(format!("%{}%", query)) + .bind(CONFIG.page_limit) + .bind(CONFIG.page_limit * page) + .fetch_all(pool) + .await?; + + Ok(rows) + } + /// List author's models pub async fn list_from_author(page: i64, author: i32) -> Result<Vec<ModelUser>, AppError> { let pool = unsafe { get_client() }; @@ -269,6 +304,23 @@ impl Model { let count: i64 = cursor.try_get(0).unwrap(); Ok(count) } + + /// Return the number of models filtered by query + pub async fn count_filter(query: String) -> Result<i64, AppError> { + let pool = unsafe { get_client() }; + let cursor = sqlx::query( + r#" + SELECT COUNT(id) as count FROM models + WHERE models.name ILIKE $1 OR description ILIKE $1 OR printer ILIKE $1 OR material ILIKE $1 + "# + ) + .bind(format!("%{}%", query)) + .fetch_one(pool) + .await?; + + let count: i64 = cursor.try_get(0).unwrap(); + Ok(count) + } } impl ModelUser { diff --git a/src/routes/model.rs b/src/routes/model.rs index d72d7af..44e2b77 100644 --- a/src/routes/model.rs +++ b/src/routes/model.rs @@ -4,7 +4,7 @@ use crate::{ models::{ auth::Claims, likes::Like, - model::{Model, ModelCreate, ModelUpload, ModelUser}, + model::{Model, ModelCreate, ModelFilter, ModelUpload, ModelUser}, user::User, }, pagination::{ModelPagination, Pagination}, @@ -21,6 +21,7 @@ use axum::{ pub fn create_route() -> Router { Router::new() .route("/", get(list_models).post(create_model)) + .route("/filter", post(filter_models)) .route("/:id", get(get_model).delete(delete_model).put(edit_model)) .route("/:id/like", post(add_like).delete(delete_like)) .route("/:id/upload", post(upload_model_file)) @@ -251,3 +252,16 @@ async fn delete_like(claims: Claims, Path(model_id): Path<i32>) -> Result<Status } } } + +/// Filter models +async fn filter_models( + pagination: Query<Pagination>, + Json(payload): Json<ModelFilter>, +) -> Result<Json<ModelPagination>, AppError> { + let page = pagination.0.page.unwrap_or_default(); + + let results = Model::filter(page, payload.q.clone()).await?; + let count = Model::count_filter(payload.q).await?; + + Ok(Json(ModelPagination { count, results })) +} |
