如何使用Diesel根据动态参数有条件地按列排序?

时间:2019-12-11 17:23:00

标签: rust rust-diesel

我试图根据外部参数为order_by指定不同的列。

这可行,但是很丑:

#[macro_use]
extern crate diesel;

use crate::diesel::prelude::*;
use diesel::pg::PgConnection;

mod schema {
    table! {
        items (id) {
            id -> Int4,
            name -> Text,
        }
    }
}

#[derive(Queryable, Debug)]
pub struct Item {
    pub id: i32,
    pub name: String,
}

fn load_items(conn: PgConnection, sort_prop: String, sort_dir: String) -> Vec<Item> {
    use schema::items::dsl::*;

    let mut query = items.into_boxed();

    // ugly: duplicating condition by sort_dir and query.order_by() calls
    query = match sort_prop.as_str() {
        "name" => {
            if sort_dir == "asc" {
                query.order_by(name.asc())
            } else {
                query.order_by(name.desc())
            }
        }
        _ => {
            if sort_dir == "asc" {
                query.order_by(id.asc())
            } else {
                query.order_by(id.desc())
            }
        }
    };

    query.load::<Item>(&conn).expect("Failed to load items")
}

fn main() {}

我的Cargo.toml有这个:

[dependencies]
diesel = { version = "1.4.3", features = ["postgres"] }

我只想按列而不是整个查询作为条件,例如:

use schema::items::dsl::*;

let mut column = match sort_prop.as_str() {
    "name" => name,
    _ => id // error: match arms have incompatible types
}

column = if sort_dir == "asc" {
    column.asc()
} else {
    column.desc()
}

let results = items
    .order_by(column)
    .load::<Item>(connection)
    .expect("Failed to load items");

这可能吗?还有其他方法可以重构吗?

我已经读过Querying a Diesel table with dynamic parameters,但这基本上是关于整个查询的条件处理,这是我要避免的事情。

我还读过Creating Diesel.rs queries with a dynamic number of .and()'s,它是关于通过过滤器进行调节的。这可能与我使用order_by所需要的接近,但是我很难将BoxableExpression的怪异性应用于我的案例,因为在文档中缺少关于我的确切案例的好的示例,也缺乏RLS支持在我的IDE中显示任何schema::items::dsl::*类型,因此我可以自己浏览。

1 个答案:

答案 0 :(得分:0)

给出以下架构:

table! {
    flights (id) {
        id -> Int4,
        name -> Text,
        country -> Text,
        launch_date -> Timestamptz,
    }
}

以及以下包含排序选项的结构:

pub struct SearchFlight {
    pub sort: Option<String>,
    pub sort_dir: Option<String>
}

可以实现以下目的:

let mut query = flights.into_boxed();
match search.sort.as_ref().map(String::as_str)  {
    Some("name") => sort_by_column(query, flights_schema::name, search.sort_dir),
    Some("country") => sort_by_column(query, flights_schema::country, search.sort_dir),
    Some("launch_date") => sort_by_column(query, flights_schema::launch_date, search.sort_dir),
    _ => query
}
query.load_page::<Flight>(con)

如果有以下功能,则为sort_by_column

fn sort_by_column<U: 'static>(mut query: BoxedQuery<'static, Pg>,
                     column: U,
                     sort_dir: Option<String>) -> BoxedQuery<'static, Pg>
    where U: ExpressionMethods + QueryFragment<Pg> + AppearsOnTable<flights_schema::table>{
    match sort_dir.as_ref().map(String::as_str) {
        Some("asc") => query.order_by(column.asc()),
        Some("desc") => query.order_by(column.desc()),
        _ => query
    }
}