Jul*_*sch 3 sql postgresql rust rust-sqlx
我正在开发一个用 Rust 编写的 REST API,使用 actix-web、SQLx 和 PostgreSQL 进行存储。假设这是我的模式(表示为 Rust 结构):
struct User {
pub id: Uuid,
pub email: String
// And so on...
}
struct Customer {
pub id: Uuid,
pub user_id: Uuid,
pub name: String,
// And so on...
}
Run Code Online (Sandbox Code Playgroud)
我当前的目标是实现一个端点,该端点返回所有用户及其嵌套的客户。即像这样:
// GET /users
// Response from endpoint
[{
"id": "uuid-1",
"email": "test@test.com",
"customers": [{
"id": "uuid-customer-1",
"name": "Customer 1"
}, {
"id": "uuid-customer-2",
"name": "Customer 2"
}]
}]
Run Code Online (Sandbox Code Playgroud)
上面的有效负载可以使用以下结构来表示:
#[derive(Serialize)]
struct CustomerData {
pub id: Uuid,
pub name: String
}
#[derive(Serialize)]
struct UserData {
pub id: Uuid,
pub email: String,
pub customers: Vec<CustomerData>
}
Run Code Online (Sandbox Code Playgroud)
使用 SQLx 宏query_as!我想出了以下解决方案尝试:
let result = sqlx::query_as!(
UserData,
r#"
SELECT U.id, U.email, array_agg((C.id, C.name)) as "customers" FROM users U
INNER JOIN customers C ON user_id = U.id
GROUP BY U.id
"#
)
.fetch_all(pool.as_ref())
.await?;
Run Code Online (Sandbox Code Playgroud)
然而,这会失败,因为 返回的结果是array_agg类型RECORD[],而 SQLx 显然尚不支持该类型。
这个问题让我想知道:
array_aggto的结果customers?前面的解释;TL;DR 在底部。
经过更多的挖掘,我找到了一个解决方案,一旦你习惯了 SQLx 和 Rust,这实际上是非常明显的。
因此,问题是 SQLx 认为 的返回值是ARRAY_AGG()类型RECORD[]。幸运的是,SQLX 允许我们通过 DSL 中的类型转换来判断我们期望的类型。
因此,为了解决这个问题,我们首先需要在查询中将我们的RECORD[]to转换为:Vec<CustomerData>
SELECT
id,
email,
ARRAY_AGG((C.id, C.name)) as "customers: Vec<CustomerData>"
FROM users
JOIN customers C ON user_id = U.id
GROUP BY id, email
Run Code Online (Sandbox Code Playgroud)
此外,我们需要实现 Trait sqlx::Typefor CustomerData. 幸运的是,有一个宏可以做到这一点:
#[derive(sqlx::Type, Serialize)]
struct CustomerData {
// ...
}
Run Code Online (Sandbox Code Playgroud)
最后但并非最不重要的一点是,还有最后一个问题需要解决:ARRAY_AGG返回NULL或数组。这可以通过三种方式解决:
SQLx 将其视为可空类型Option<T>。所以该字段应该是Struct 中的customers类型。Option<Vec<CustomerData>>
通过在查询转换中使用感叹号,告诉 SQLx 不要担心,要高兴并断言该值不为空。即像这样:... as "customers!: Vec<CustomerData>
使用SQL返回一个空数组(其他解决方案可以在这里找到),当ARRAY_AGG返回NULL时:
SELECT
id,
email,
COALESCE(NULLIF(ARRAY_AGG((C.id, C.name)), '{NULL}'), '{}') as "customers: Vec<CustomerData>"
FROM users
JOIN customers C ON user_id = U.id
GROUP BY id, email
Run Code Online (Sandbox Code Playgroud)
如果层次结构深度不超过 1,则此解决方案有效。如果嵌套更深,您将因 SQLx 类型解析器中的错误而碰壁。与此相关的问题可以在这里找到。
长话短说:
sqlx::Type嵌套类型。SELECT
ARRAY_AGG(JOINED.id) as "field_name!: Vec<AggregateType>"
FROM ...
Run Code Online (Sandbox Code Playgroud)
编辑:令我沮丧的是,自定义枚举还算作层次结构中的一个级别。因此,如果您的嵌套类型具有枚举,则不支持这种情况。即使对于当前版本 0.7.1。
| 归档时间: |
|
| 查看次数: |
1602 次 |
| 最近记录: |