Как сделать динамичные фильтры на rust diesel?

Есть такая функция написанная на actix-web:
#[get("/")]
async fn get_list(
  pool: web::Data<DatabasePool>,
  list: web::Json::<List>
  ) -> Result<HttpResponse, ApiError> {
  use schema::users::dsl::*;
  process_request!(get_list, users, Users, pool, list.0)
}


она принимает лист List такого вида
#[derive(Deserialize)]
pub struct List {
    pub offset: Option<i64>,
    pub limit: Option<i64>,
    pub sort_by: Option<SortBy>,
    pub filters: Option<HashMap<String, serde_json::Value>>
}

далее в макросе process_request у нас запускается макрос get list, который применяет offset, limit, sort_by, но как приминить filter, я писал такой макрос для этого, но ничего не вышло:
macro_rules! make_filter {
  ($query:expr, $filters:expr) => {
        $query = match &$filters {
            Some(filter) => {
                let mut query = $query;
                for (key, value) in filter {
                    query = query.filter(diesel::dsl::sql(&format!(r#""{}"{}"#, key, value)));
                }
                query
            },
            _ => $query
        };
  };
 }


нужна аннотация типов, как это сделать? у меня много таблиц и в каждой много полей с разным типами данных, я хочу чтобы у меня был универсальный макрос который мог обрабатывать все фильтры для всех таблиц.
  • Вопрос задан
  • 70 просмотров
Пригласить эксперта
Ответы на вопрос 1
Torin_Asakura
@Torin_Asakura
Lead Architect
Дмитрий Беляев прав, тут действительно лучше использовать функции вместо макросов. Макросы в Rust штука довольно мощная, но проигрывает функциям по гибкости, особенно когда дело доходит до сложной логики и типизации.

Давай прикинем решение твоей задачки. У дизеля есть такая штука как трейты. Вот коленочный пример:

use diesel::prelude::*;
use serde_json::Value;
use std::collections::HashMap;

fn apply_filters<T>(mut query: T, filters: &HashMap<String, Value>) -> T
where
    T: diesel::query_builder::AsQuery,
    T::Query: diesel::query_builder::QueryFragment<diesel::pg::Pg> + Query,
{
    for (key, value) in filters {
        match value {
            Value::String(s) => {
                query = query.filter(diesel::dsl::sql(&format!("{} = '{}'", key, s)));
            },
            // Сюда можно закинуть остальные типы
            _ => {}
        }
    }
    query
}

async fn get_list(
    pool: web::Data<DatabasePool>,
    list: web::Json<List>
) -> Result<HttpResponse, ApiError> {
    use schema::users::dsl::*;

    let base_query = users.into_boxed(); // Пример базового запроса
    let query_with_filters = apply_filters(base_query, &list.filters.unwrap_or_default());
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы