选择与包含
使用 with/fetch 来获取关联是可以的,但在类型安全性方面还有待改进。选择与包含查询构建器函数和宏可以帮助解决这个问题,为每个您选择获取的字段和关联提供精确的类型。
Select 还提供了仅获取特定字段的功能,而 include 将获取所有标量字段和任何指定的关联。
示例使用以下模式
model Post {
id String @id @default(cuid())
title String
comments Comment[]
}
model Comment {
id String @id @default(cuid())
content String
post Post @relation(fields: [postID], references: [id])
postID String
}
设置
select!
和 include!
依赖于递归并在内部相互调用,因此它们必须知道生成的客户端的模块路径。默认情况下,这是 crate::prisma
,它假设您的客户端生成到一个名为 prisma.rs
的文件,并且位于它所在的板条箱的根目录。如果您已将客户端配置为具有不同的名称或位于其他位置,则需要通过 module_path
生成器选项提供此位置。
module_path
是相对于 crate
的 Rust 路径,指向您生成的客户端。
generator client {
provider = "cargo prisma"
output = "./src/generated/db.rs"
// `select` macros will now point to `crate::generated::db`
// instead of `crate::prisma`
module_path = "generated::db"
}
基础
select!
和 include!
在语法上非常相似,它们唯一的区别在于它们做什么以及不做什么。每个模型模块都包含 select!
和 include!
宏,其结果可以提供给它们各自的查询构建器函数。
要获取的字段可以指定为 {}
内部的空格分隔列表
post::select!({
id // select can pick individual fields
title
})
// Above will generate
struct Data {
id: String,
title: String,
}
post::include!({
comments // include can only pick relations
})
// Above will generate
struct Data {
id: String,
title: String,
comments: Vec<comment::Data>
}
嵌套选择
select
和 include
也可以在获取关联时应用,实际上可以应用到任何深度。只需添加一个 :
,指定您是否要在关联上选择或包含,并添加您的选择
// Nested include inside select
post::select!({
comments: include {
post
}
})
// Above will generate
struct Data {
comments: comments::Data // refers to below module
}
// Module + data struct generated for nested selection
mod comments {
pub struct Data {
post: post::Data
}
}
// Nested select inside include
post::include!({
comments: select {
content
}
})
// Above will generate
struct Data {
id: String,
title: String,
comments: comments::Data // refers to below module
}
// Module + data struct generated for nested selection
mod comments {
pub struct Data {
content: String
}
}
多关系选项
在获取多关系时,获取语句可以充当对 model::field::fetch
的等效调用,允许进行过滤、分页和排序。这在 select!
和 include!
中都有效。
post::select!({
// Equivalent to post::comments::fetch(..) ..
comments(vec![comment::content::contains("prisma".to_string())])
.skip(5)
.take(10): select { // You can add on nested selections too!
id
content
}
})
在查询中使用
只需将 select!
或 include!
的结果传递给等效的查询构建器函数
// Type is anonymous and does not exist outside of the call to `include`
let posts: Vec<_> = client
.post()
.find_many(vec![])
.include(post::include!({
comments: select {
id
}
}))
.exec()
.await?;
// Generated type is equivalent to
struct Data {
id: String,
title: String,
comments: comments::Data
}
mod comments {
pub struct Data {
id: String
}
}
在查询之外使用类型
在某些情况下,访问 select!
和 include!
生成的类型在对查询构建器函数的调用之外可能很有用,例如,如果您想从函数返回查询的结果。
这可以通过在根选择之前添加名称来完成。这将导致生成一个具有该名称的模块,并且该模块将包含一个 Data
结构体以及一个 include
或 select
函数,具体取决于您使用哪个宏。
post::select!(post_only_title {
title
})
async fn do_query() -> Vec<post_only_title::Data> {
client
.post()
.find_many(vec![])
.select(post_only_title::select())
.exec()
.await
.unwrap()
}
// Generated type is equivalent to
pub mod post_only_title {
pub struct Data {
title: String
}
pub fn select() // return type is an internal detail
}
传递参数
在内联执行选择时,外部值可以很好地用作参数,因为它们可以从宏外部捕获。当选择在查询构建器函数外部声明时,无法捕获此上下文。这就是为什么select
和 include
函数不仅仅是结构体,它们可以使用以下语法传递在宏中定义的参数
post::include!((filters: Vec<comment::WhereParam>, skip: i64, take: i64) => post_with_comments {
comments(filters).skip(skip).take(take)
})
async fn do_query() -> Vec<post_only_title::Data> {
let filters = vec![comment::content::contains("prisma".to_string())];
let skip = 5;
let take = 5;
client
.post()
.find_many(vec![])
.select(post_only_title::select(filters, skip, take))
.exec()
.await
.unwrap()
}
// Generated type is equivalent to
pub mod post_with_comments {
pub struct Data {
id: String,
title: String,
comments: Vec<comment::Data>
}
pub fn select(filters: Vec<comment::WhereParam>, skip: i64, take: i64) // return type is an internal detail
}