在 Prisma 中创建或更新一对多关系

时间:2021-07-07 11:33:24

标签: mysql typescript prisma

我正在尝试更新 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;
    }[];
}

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

2 个答案:

答案 0 :(得分:1)

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

更改架构

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 以了解有关多对多关系的更多信息。

编写更新查询

同样,根据您在评论中提供的说明,更新操作将执行以下操作:

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

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

您可以使用两个单独的 update 查询来执行此操作。第一次更新将断开所有先前连接的 features 为所提供的 A_User。第二个查询将连接或创建 features 表中新提供的 A_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 中的 嵌套写入 部分文档。

答案 1 :(得分:0)

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
]);