【问题标题】:Create or update one to many relationship in Prisma在 Prisma 中创建或更新一对多关系
【发布时间】:2021-09-17 22:50:40
【问题描述】:

我正在尝试更新 Prisma 中的一对多关系。我的架构看起来像这样

model A_User {
  id            Int          @id
  username      String
  age           Int
  bio           String       @db.VarChar(1000)
  createdOn     DateTime     @default(now())
  features      A_Features[]
}

model A_Features {
  id       Int     @id @default(autoincrement())
  description    String
  A_User   A_User? @relation(fields: [a_UserId], references: [id])
  a_UserId Int?
}

我正在尝试向 id: 1 的用户添加一些新功能,或者如果它们已经存在则更新它们。

我正在尝试做类似的事情

const post = await prisma.a_User.update({
        where: { id: 1},
        data: { 
            features: {
                upsert: [
                    { description: 'first feature'},
                    { description: 'second feature'}
                ]
            }
        }
    })

编译器不高兴,它告诉我

Type '{ features: { upsert: { description: string; }[]; }; }' is not assignable to type '(Without<A_UserUpdateInput, A_UserUncheckedUpdateInput> & A_UserUncheckedUpdateInput) | (Without<...> & A_UserUpdateInput)'.
  Object literal may only specify known properties, and 'features' does not exist in type '(Without<A_UserUpdateInput, A_UserUncheckedUpdateInput> & A_UserUncheckedUpdateInput) | (Without<...> & A_UserUpdateInput)'.ts(2322)
index.d.ts(1572, 5): The expected type comes from property 'data' which is declared here on type '{ select?: A_UserSelect; include?: A_UserInclude; data: (Without<A_UserUpdateInput, A_UserUncheckedUpdateInput> & A_UserUncheckedUpdateInput) | (Without<...> & A_UserUpdateInput); where: A_UserWhereUniqueInput; }'
(property) features: {
    upsert: {
        description: string;
    }[];
}

我不知道该怎么做,也无法在文档中找到明确的帮助。关于如何实现它或在哪里可以找到一些示例的任何想法?

【问题讨论】:

  • 能否请您澄清“如果它们已经存在则更新它们”的意思。如果它们已经存在于特定A_Userfeatures 关系中,或者它们是否存在于A_Features 表中的任何地方?此外,如果您尝试添加的功能之一已经存在,究竟您希望该特定功能如何“更新”?
  • 我对这个后端的东西很陌生,如果不清楚,请道歉。我认为我需要做的是检查具有该特定描述的功能是否存在,如果不存在,我需要创建它并将其添加给用户,如果确实存在,我只需要添加它给用户。通过“更新”,我想这意味着删除一个功能并在其位置插入另一个功能。你的评论已经让我明白我一直在以错误的方式看待这个问题。谢谢
  • 好的,这有帮助。再澄清一点。您是在尝试“附加”还是“覆盖”现有功能。例如,假设 Userid 为 1 已经有两个 features 分别带有 description "feature1" 和 "feature2"。现在您在User 上运行更新操作,id 为 1,提供features:“feature2”和“feature3”。更新后Userid 为1 的features 的期望最终状态是什么?请记住,在您进行更新操作之前,“feature2”已经存在。
  • 在上述情况下,最终状态将是“feature2”和“feature3”(“feature2”不会改变,而“feature1”被“feature3”覆盖)。
  • 谢谢,还有一个问题,我可以提供一个具体的解决方案。两个不同的A_User 记录可以在其features 中具有完全相同的description 字段的A_Features 记录吗?换一种说法,带有某个descriptionA_Features 记录对于一个A_User 记录是唯一的还是可以连接到许多A_User 记录?

标签: mysql typescript prisma


【解决方案1】:

我正在根据您在 cmets 中提供的说明提供我的解决方案。首先,我将对您的架构进行以下更改。

改变架构

model A_User {
  id        Int          @id
  username  String
  age       Int
  bio       String       @db.VarChar(1000)
  createdOn DateTime     @default(now())
  features  A_Features[]
}

model A_Features {
  id          Int      @id @default(autoincrement())
  description String   @unique
  users       A_User[]
}

值得注意的是,A_UserA_Features 之间的关系现在是多对多的。因此,单个 A_Features 记录可以连接到多个 A_User 记录(反之亦然)。

此外,A_Features.description 现在是唯一的,因此可以仅使用它的描述来唯一地搜索某个功能。

您可以阅读Prisma Guide on Relations 以了解有关多对多关系的更多信息。

编写更新查询

再次,根据您在 cmets 中提供的说明,更新操作将执行以下操作:

  • 覆盖features 记录中的现有A_User。因此,任何以前的features 都将断开连接并替换为新提供的features。请注意,之前的 features 不会从 A_Features 表中删除,但它们只会从 A_User.features 关系中断开

  • 创建A_Features表中尚不存在的新提供的功能,并连接A_Features表中已存在的提供的功能。

您可以使用两个单独的update 查询来执行此操作。第一次更新将为提供的A_User断开所有以前连接的features。第二个查询将连接或创建A_Features 表中新提供的features。最后,您可以使用transactions API 来确保这两个操作按顺序一起发生。事务 API 将确保如果两个更新中的任何一个出现错误,那么两个更新都会失败并被数据库回滚。


//inside async function
const disconnectPreviouslyConnectedFeatures =  prisma.a_User.update({
    where: {id: 1},
    data: {
        features: {
            set: []  // disconnecting all previous features
        }
    }
})

const connectOrCreateNewFeatures =  prisma.a_User.update({
    where: {id: 1},
    data: {
        features: {
            // connect or create the new features
            connectOrCreate: [
                {
                    where: {
                        description: "'first feature'"
                    }, create: {
                        description: "'first feature'"
                    }
                },
                {
                    where: {
                        description: "second feature"
                    }, create: {
                        description: "second feature"
                    }
                }
            ]
        }
    }
})

// transaction to ensure either BOTH operations happen or NONE of them happen.
await prisma.$transaction([disconnectPreviouslyConnectedFeatures, connectOrCreateNewFeatures ])

如果您想更好地了解connectdisconnectconnectOrCreate 的工作原理,请阅读文档中Prisma Relation queries article嵌套写入部分。

【讨论】:

  • 这太棒了,您帮助我意识到我的方法出了什么问题,我将更深入地了解您链接的文档。感谢您为此付出的时间。
  • 不客气。乐意效劳!如果它解决了您的问题,请考虑将其标记为正确的解决方案,以便其他人将来可以参考。
  • 嗨,Tasin,你觉得你可以看看这个stackoverflow.com/questions/68964434/…吗?
  • 嘿,@Riccardo,真的很抱歉回复晚了,由于个人原因,最近几周我有点忙。你能解决问题吗?
  • 嗨,Tasin,您对使用disconnect: [] 代替set: [] 有何看法?当我使用set: [] 时,我似乎仍然在很多方面清除了其他相关记录。
【解决方案2】:

prisma.a_User.update 的 TypeScript 定义可以准确地告诉您它需要哪些选项。这将告诉您为什么会发生 'features' does not exist in type 错误。我想你传递给data 的对象与你指定的选项不同;如果您可以检查 TypeScript 类型,Prisma 会准确告诉您可用的选项。

如果您尝试添加新功能并更新特定功能,则需要指定 Prisma 如何找到旧功能(如果存在)以更新该功能。 Upsert 不会以您当前使用的方式工作;您需要为 upsert 调用提供某种标识符,以便确定您要添加的功能是否已经存在。

https://www.prisma.io/docs/reference/api-reference/prisma-client-reference/#upsert

您至少需要create(如果该功能不存在则传递什么数据)、update(如果该功能存在则传递什么数据)和where(Prisma 如何找到您想要更新或创建。)

您还需要多次拨打upsert;一个用于您要更新或创建的每个功能。在这种情况下,您可以将呼叫与Promise.all 一起批处理。

const upsertFeature1Promise = prisma.a_User.update({
  data: {
    // upsert call goes here, with "create", "update", and "where"
  }
});
const upsertFeature2Promise = prisma.a_User.update({
  data: {
    // upsert call goes here, with "create", "update", and "where"
  }
});
const [results1, results2] = await Promise.all([
  upsertFeaturePromise1,
  upsertFeaturePromise2
]);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-11
    • 2021-07-23
    • 2021-12-04
    • 2021-08-24
    • 2022-10-25
    • 2022-01-19
    • 2021-07-25
    • 2022-01-27
    相关资源
    最近更新 更多