Skip to main content

`result`:添加自定义字段和方法来查询结果

info

Prisma 客户端扩展从版本 4.16.0 及更高版本开始正式可用。它们是在 4.7.0 版本的预览版中引入的。如果你运行的版本早于 4.16.0,请确保启用 clientExtensions Preview 功能标志。

¥Prisma Client extensions are Generally Available from versions 4.16.0 and later. They were introduced in Preview in version 4.7.0. Make sure you enable the clientExtensions Preview feature flag if you are running on a version earlier than 4.16.0.

你可以使用 result Prisma 客户端扩展 组件类型添加自定义字段和方法来查询结果。

¥You can use the result Prisma Client extensions component type to add custom fields and methods to query results.

使用 $extends 客户级方法 创建扩展客户端。扩展客户端是标准 Prisma 客户端的一种变体,由一个或多个扩展封装。

¥Use the $extends client-level method to create an extended client. An extended client is a variant of the standard Prisma Client that is wrapped by one or more extensions.

要将自定义 fieldmethod 添加到查询结果,请使用以下结构。在此示例中,我们将自定义字段 myComputedField 添加到 user 模型查询的结果中。

¥To add a custom field or method to query results, use the following structure. In this example, we add the custom field myComputedField to the result of a user model query.

const prisma = new PrismaClient().$extends({
name?: 'name',
result?: {
user: { // in this case, we extend the `user` model
myComputedField: { // the name of the new computed field
needs: { ... },
compute() { ... }
},
},
},
});

参数如下:

¥The parameters are as follows:

  • name:(可选)指定错误日志中显示的扩展名。

    ¥name: (optional) specifies a name for the extension that appears in error logs.

  • result:为查询结果定义新的字段和方法。

    ¥result: defines new fields and methods to the query results.

  • needs:描述结果字段依赖的对象。

    ¥needs: an object which describes the dependencies of the result field.

  • compute:定义访问虚拟字段时如何计算虚拟字段的方法。

    ¥compute: a method that defines how the virtual field is computed when it is accessed.

查询结果添加自定义字段

¥Add a custom field to query results

你可以使用 result 扩展组件向查询结果添加字段。这些字段在运行时计算并且是类型安全的。

¥You can use the result extension component to add fields to query results. These fields are computed at runtime and are type-safe.

在以下示例中,我们将一个名为 fullName 的新虚拟字段添加到 user 模型中。

¥In the following example, we add a new virtual field called fullName to the user model.

const prisma = new PrismaClient().$extends({
result: {
user: {
fullName: {
// the dependencies
needs: { firstName: true, lastName: true },
compute(user) {
// the computation logic
return `${user.firstName} ${user.lastName}`
},
},
},
},
})

const user = await prisma.user.findFirst()

// return the user's full name, such as "John Doe"
console.log(user.fullName)

在上面的例子中,compute 的输入 user 是根据 needs 中定义的对象自动键入的。firstNamelastName 属于 string 类型,因为它们是在 needs 中指定的。如果 needs 中未指定,则无法访问它们。

¥In above example, the input user of compute is automatically typed according to the object defined in needs. firstName and lastName are of type string, because they are specified in needs. If they are not specified in needs, then they cannot be accessed.

在另一个计算字段中重复使用一个计算字段

¥Re-use a computed field in another computed field

以下示例以类型安全的方式计算用户的头衔和全名。titleFullName 是重复使用 fullName 计算字段的计算字段。

¥The following example computes a user's title and full name in a type-safe way. titleFullName is a computed field that reuses the fullName computed field.

const prisma = new PrismaClient()
.$extends({
result: {
user: {
fullName: {
needs: { firstName: true, lastName: true },
compute(user) {
return `${user.firstName} ${user.lastName}`
},
},
},
},
})
.$extends({
result: {
user: {
titleFullName: {
needs: { title: true, fullName: true },
compute(user) {
return `${user.title} (${user.fullName})`
},
},
},
},
})

字段注意事项

¥Considerations for fields

  • 出于性能原因,Prisma 客户端在访问时计算结果,而不是在检索时计算结果。

    ¥For performance reasons, Prisma Client computes results on access, not on retrieval.

  • 你只能创建基于标量字段的计算字段。

    ¥You can only create computed fields that are based on scalar fields.

  • 你只能将计算字段与 select 一起使用,并且不能聚合它们。例如:

    ¥You can only use computed fields with select and you cannot aggregate them. For example:

    const user = await prisma.user.findFirst({
    select: { email: true },
    })
    console.log(user.fullName) // undefined

向结果对象添加自定义方法

¥Add a custom method to the result object

你可以使用 result 组件添加查询结果的方法。以下示例将一个新方法 save 添加到结果对象。

¥You can use the result component to add methods to query results. The following example adds a new method, save to the result object.

const prisma = new PrismaClient().$extends({
result: {
user: {
save: {
needs: { id: true },
compute(user) {
return () =>
prisma.user.update({ where: { id: user.id }, data: user })
},
},
},
},
})

const user = await prisma.user.findUniqueOrThrow({ where: { id: someId } })
user.email = 'mynewmail@mailservice.com'
await user.save()

使用 result 扩展组件的 omit 查询选项

¥Using omit query option with result extension component

你可以将 omit(预览)选项自定义字段 以及自定义字段所需的字段一起使用。

¥You can use the omit (Preview) option with custom fields and fields needed by custom fields.

查询结果中的自定义字段所需的 omit 字段

¥omit fields needed by custom fields from query result

如果你 omit 一个自定义字段的依赖,即使它不会包含在查询结果中,它仍将从数据库中读取。

¥If you omit a field that is a dependency of a custom field, it will still be read from the database even though it will not be included in the query result.

以下示例省略了 password 字段,它是自定义字段 sanitizedPassword 的依赖:

¥The following example omits the password field, which is a dependency of the custom field sanitizedPassword:

const xprisma = prisma.$extends({
result: {
user: {
sanitizedPassword: {
needs: { password: true },
compute(user) {
return sanitize(user.password)
},
},
},
},
})

const user = await xprisma.user.findFirstOrThrow({
omit: {
password: true,
},
})

在这种情况下,尽管结果中省略了 password,但仍会从数据库中查询它,因为它是 sanitizedPassword 自定义字段的依赖。

¥In this case, although password is omitted from the result, it will still be queried from the database because it is a dependency of the sanitizedPassword custom field.

查询结果中的 omit 自定义字段和依赖

¥omit custom field and dependencies from query result

要确保完全不从数据库中查询省略的字段,你必须省略自定义字段及其依赖。

¥To ensure omitted fields are not queried from the database at all, you must omit both the custom field and its dependencies.

以下示例省略了自定义字段 sanitizedPassword 和依赖 password 字段:

¥The following example omits both the custom field sanitizedPassword and the dependent password field:

const xprisma = prisma.$extends({
result: {
user: {
sanitizedPassword: {
needs: { password: true },
compute(user) {
return sanitize(user.password)
},
},
},
},
})

const user = await xprisma.user.findFirstOrThrow({
omit: {
sanitizedPassword: true,
password: true,
},
})

在这种情况下,省略 passwordsanitizedPassword 将从结果中排除两者,并阻止从数据库中读取 password 字段。

¥In this case, omitting both password and sanitizedPassword will exclude both from the result as well as prevent the password field from being read from the database.

限制

¥Limitation

截至目前,Prisma Client 的结果扩展组件不支持关系字段。这意味着你无法基于相关模型或关系关系中的字段创建自定义字段或方法(例如,user.posts、post.author)。needs 参数只能引用同一模型中的标量字段。跟随 GitHub 上的问题 #20091

¥As of now, Prisma Client's result extension component does not support relation fields. This means that you cannot create custom fields or methods based on related models or fields in a relational relationship (e.g., user.posts, post.author). The needs parameter can only reference scalar fields within the same model. Follow issue #20091 on GitHub.

const prisma = new PrismaClient().$extends({
result: {
user: {
postsCount: {
needs: { posts: true }, // This will not work because posts is a relation field
compute(user) {
return user.posts.length; // Accessing a relation is not allowed
},
},
},
},
})