Prisma Datamodel:主键作为两个关系模型的组合
Prisma Datamodel: Primary key as a combination of two relational models
我在 Prisma 数据建模中遇到问题,我必须 限制用户只能为产品提交一条评论 。我有针对非约束情况的以下设计。
Should Customer
and Product
be combined into a primary key in ProductReview
model, or should this constraint be imposed at the application server level, and not at the database level?
现在的数据模型(非约束版本):
type Product {
id: ID! @unique
title: String!
reviews: [ProductReview!]! @relation(name: "ProductReviews", onDelete: CASCADE)
}
type Customer {
id: ID! @unique
email: String @unique
}
type ProductReview {
id: ID! @unique
forProduct: Product! @relation(name: "ProductReviews", onDelete: SET_NULL)
byCustomer: Customer!
review: String!
ratinng: Float!
}
我会从MySQL的角度来回答。如果你想强制给定客户只能与给定产品关联一次,那么你应该在 ProductReview
table:
ALTER TABLE ProductReview ADD UNIQUE KEY uk_cust_prod (customer_id, product_id);
这意味着,当这种关系已经存在时,任何为给定客户和产品插入记录的尝试都将在数据库级别失败。
如果您还想为此添加应用程序级别的检查,您当然可以这样做,或许可以先在那里处理。
I have to constrain that a user can submit only one review for a product. I have following design for the non-constrained situation.
遗憾的是,Prisma 目前无法做到这一点。已经有一个 open feature request 要求此功能,请留下您的问题!
要在您的应用程序中获得该功能,您需要在应用程序层(例如 express、apollo-server 或 graphql-yoga)手动实施该约束。
您可以查看 How to GraphQL 的 this 页面,其中存在与 User
、Link
和 Vote
类型类似的情况。以下是如何使用 graphql-yoga 实现创建 Vote
并确保没有来自该用户的投票的解析器:
async function vote(parent, args, context, info) {
// 1
const userId = getUserId(context)
// 2
const linkExists = await context.db.exists.Vote({
user: { id: userId },
link: { id: args.linkId },
})
if (linkExists) {
throw new Error(`Already voted for link: ${args.linkId}`)
}
// 3
return context.db.mutation.createVote(
{
data: {
user: { connect: { id: userId } },
link: { connect: { id: args.linkId } },
},
},
info,
)
}
有一个解决方法。实现像 SQL 这样的多个主键的概念。
这个想法很简单,在 "ProductReview" 下再创建一个名为 "UniqueCustomerReview" 的字段。在突变时将 "UniqueCustomerReview" 值设置为“[customerEmail]_[productID]”。所以我们现在可以使用 prisma 的默认唯一性。
您的数据模型将如下所示:
type Product {
id: ID! @unique
title: String!
reviews: [ProductReview!]! @relation(name: "ProductReviews", onDelete: CASCADE)
}
type Customer {
id: ID! @unique
email: String @unique
}
type ProductReview {
id: ID! @unique
forProduct: Product! @relation(name: "ProductReviews", onDelete: SET_NULL)
byCustomer: Customer!
review: String!
ratinng: Float!
UniqueCustomerReview:String! # adding a extra field
}
正在创建或变更查询:
mutation{
createProductReview(
data:{
forProduct: {"connect":{"id":"<Replacec_with_product_id>"}}
byCustomer: {"connect":{"email":"<Replacec_with_customer_email>"}}
review: "my product review..."
ratinng: 5.0
UniqueCustomerReview:"loggedInUser@email.com_<Poductid>" # replace the string with user email and product id. this will create a unique product review for the user alone.
}
)
{
UniqueCustomerReview
# ... any requied fields
}
}
在我的例子中,在 id
上进行字符串连接就足够了
所以 id
例如产品 #120 和客户 #15 的“120-15”
看起来 Prisma v2 引入了复合主键:
https://newreleases.io/project/github/prisma/prisma/release/2.0.0-preview023
link中的一个例子:
model User {
firstName String
lastName String
email String
@@id([firstName, lastName])
}
所以在给定的问题示例中,应该可以添加到 ProductReview
:
@@id([id, forProduct])
我在 Prisma 数据建模中遇到问题,我必须 限制用户只能为产品提交一条评论 。我有针对非约束情况的以下设计。
Should
Customer
andProduct
be combined into a primary key inProductReview
model, or should this constraint be imposed at the application server level, and not at the database level?
现在的数据模型(非约束版本):
type Product {
id: ID! @unique
title: String!
reviews: [ProductReview!]! @relation(name: "ProductReviews", onDelete: CASCADE)
}
type Customer {
id: ID! @unique
email: String @unique
}
type ProductReview {
id: ID! @unique
forProduct: Product! @relation(name: "ProductReviews", onDelete: SET_NULL)
byCustomer: Customer!
review: String!
ratinng: Float!
}
我会从MySQL的角度来回答。如果你想强制给定客户只能与给定产品关联一次,那么你应该在 ProductReview
table:
ALTER TABLE ProductReview ADD UNIQUE KEY uk_cust_prod (customer_id, product_id);
这意味着,当这种关系已经存在时,任何为给定客户和产品插入记录的尝试都将在数据库级别失败。
如果您还想为此添加应用程序级别的检查,您当然可以这样做,或许可以先在那里处理。
I have to constrain that a user can submit only one review for a product. I have following design for the non-constrained situation.
遗憾的是,Prisma 目前无法做到这一点。已经有一个 open feature request 要求此功能,请留下您的问题!
要在您的应用程序中获得该功能,您需要在应用程序层(例如 express、apollo-server 或 graphql-yoga)手动实施该约束。
您可以查看 How to GraphQL 的 this 页面,其中存在与 User
、Link
和 Vote
类型类似的情况。以下是如何使用 graphql-yoga 实现创建 Vote
并确保没有来自该用户的投票的解析器:
async function vote(parent, args, context, info) {
// 1
const userId = getUserId(context)
// 2
const linkExists = await context.db.exists.Vote({
user: { id: userId },
link: { id: args.linkId },
})
if (linkExists) {
throw new Error(`Already voted for link: ${args.linkId}`)
}
// 3
return context.db.mutation.createVote(
{
data: {
user: { connect: { id: userId } },
link: { connect: { id: args.linkId } },
},
},
info,
)
}
有一个解决方法。实现像 SQL 这样的多个主键的概念。 这个想法很简单,在 "ProductReview" 下再创建一个名为 "UniqueCustomerReview" 的字段。在突变时将 "UniqueCustomerReview" 值设置为“[customerEmail]_[productID]”。所以我们现在可以使用 prisma 的默认唯一性。
您的数据模型将如下所示:
type Product {
id: ID! @unique
title: String!
reviews: [ProductReview!]! @relation(name: "ProductReviews", onDelete: CASCADE)
}
type Customer {
id: ID! @unique
email: String @unique
}
type ProductReview {
id: ID! @unique
forProduct: Product! @relation(name: "ProductReviews", onDelete: SET_NULL)
byCustomer: Customer!
review: String!
ratinng: Float!
UniqueCustomerReview:String! # adding a extra field
}
正在创建或变更查询:
mutation{
createProductReview(
data:{
forProduct: {"connect":{"id":"<Replacec_with_product_id>"}}
byCustomer: {"connect":{"email":"<Replacec_with_customer_email>"}}
review: "my product review..."
ratinng: 5.0
UniqueCustomerReview:"loggedInUser@email.com_<Poductid>" # replace the string with user email and product id. this will create a unique product review for the user alone.
}
)
{
UniqueCustomerReview
# ... any requied fields
}
}
在我的例子中,在 id
所以 id
例如产品 #120 和客户 #15 的“120-15”
看起来 Prisma v2 引入了复合主键:
https://newreleases.io/project/github/prisma/prisma/release/2.0.0-preview023
link中的一个例子:
model User {
firstName String
lastName String
email String
@@id([firstName, lastName])
}
所以在给定的问题示例中,应该可以添加到 ProductReview
:
@@id([id, forProduct])