summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSanto Cariotti <santo@dcariotti.me>2022-10-03 19:48:52 +0000
committerSanto Cariotti <santo@dcariotti.me>2022-10-03 19:48:52 +0000
commitcd316de7293f5a31f125fb62611af4df7259aff4 (patch)
tree66c790751cd1943a4664796023004792dd1fd156
parentec88959f1cb264ac9725636bd3f3c24474c0ab54 (diff)
Add model search by fields
-rw-r--r--src/models/model.rs52
-rw-r--r--src/routes/model.rs16
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 }))
+}