【问题标题】:How to cascade deletes in GraphQL?GraphQL 级联删除
【发布时间】:2018-02-19 22:46:58
【问题描述】:

我正在使用 GraphQL,并希望从数据库中删除实体以及通过关系分配给第一个实体的其他实体。

假设我在 GraphQL Schema 中有三种类型

  1. 用户
  2. 作业
  3. 任务

逻辑如下:User 在某种程度上与Task 相关。这种关系由“中间”对象Assignment 表示。 (在Assignment 中,我可以将用户设置为任务或工作人员或其他任何东西的主管,这不是重点。)

现在,我想删除User和相关的AssignmentTask不应该被删除)。

我想知道我是否可以通过只执行一个只有一个参数的突变查询来做到这一点:用户 ID?

我在想这样的事情:

mutation deleteUser($userId: ID!){
  deleteUser(id: $userId){
    id
    assignment {
      id # use that id somehow below
    }
  } {
    deleteAssignment(id: id_from_above) {
    }
  }
}

这样的事情可能吗?

【问题讨论】:

  • 下面提供的答案对您有用吗?如果没有,请发表评论,我很乐意改进。如果是,你能接受吗?

标签: graphql


【解决方案1】:

我认为你会从阅读Mutation section of the GraphQL spec 中受益。

如果操作是突变,则操作的结果是在突变根对象类型上执行突变的顶级选择集的结果。这个选择集应该是串行执行的。

这里的关键是突变是:

  • 被认为是一种查询,但它们有副作用
  • 它们不是按方便的顺序完成,甚至不是同时进行(如查询),而是按顺序完成

具体说明您的问题:如果删除用户在概念上还需要删除该用户的分配,并且删除分配会删除所有任务,那么暴露的突变(即“查询”)可能只是:

mutation deleteUser($userId: ID!) {
  deleteUser(id: $userId)
}

相关的删除只是发生了,不需要返回任何东西。如果您确实想要退回东西,您可以添加这些东西以供查看:

mutation deleteUser($userId: ID!) {
  deleteUser(id: $userId) {
    assignment {
      task
    }
  }
}

或者,如果您希望分配和任务的删除由客户端控制,以便“查看”子删除(触发此类实际删除:

mutation deleteUser($userId: ID!) {
  deleteUser(id: $userId) {
    deleteAssignment {
      deleteTask
    }
  }
}

当然,假设您适当地定义了 Mutation 类型以使这些字段可用,并且基础软件会根据上述要求的行为进行相应的操作。

【讨论】:

  • 变异不能像查询一样嵌套,所以最后一个例子要么是错误的,要么需要子查询实际上是mutations
【解决方案2】:

如果您只是想级联删除,则客户端无需执行任何操作 - 无需特殊查询。只需在突变解析器中执行适当的逻辑即可。

例如如果您有一个用于删除突变解析器最终将调用的用户的服务方法(示例是 Java,但无论您使用哪种语言(您没有提到),逻辑都是相同的):

boolean deleteUser(String id) {
   // either do the assignment deletion yourself here (not good)
   // or set your database to cascade the deletions (preferable)
   dataBase.execute("DELETE FROM User WHERE id = :id");
   return true; //have to return *something*
}

客户端不需要关心这个,他们只是告诉你的系统删除用户:

mutation deleteUser($userId: ID!){
  deleteUser(id: $userId)
}

如果您希望客户端能够获得比布尔成功标志更好的东西,请返回该东西(这当然意味着相应地更改架构):

String deleteUser(String id) {       
   dataBase.execute("DELETE FROM User WHERE id = :id");
   //return the e.g. the ID
   return id;
}

String deleteUser(String id) { 
   User user = dataBase.execute("SELECT FROM User WHERE id = :id");   
   dataBase.execute("DELETE FROM User WHERE id = :id");
   //return the whole deleted user
   return user;
}

后者使客户端能够查询结果(这些是子查询,不是子突变,没有子突变之类的东西):

mutation deleteUser($userId: ID!){
  deleteUser(id: $userId) {
    id
    assignments {
      id
    }
  }
}

需要注意的是,与查询不同,突变不能嵌套,但是是的,您可以发送多个顶级突变(如您的示例中所示)。不幸的是,没有办法将第一个的结果用作第二个的输入。有人要求在 GraphQL 规范中引入这一点,但它可能会发生,也可能不会发生。

意思是你的例子:

mutation deleteUser($userId: ID!) {
  deleteUser(id: $userId) {
    id
    assignment {
      id # use that id somehow below
    }
  } {

  deleteAssignment(id: id_from_above) {
    id
  }
}

很遗憾,这是不可能的。

您必须以两个单独的请求 的方式执行此操作,或者提出更精细的方法。如果您需要允许客户端进行更深层次的控制,您可以做的是接受更复杂的输入,而不仅仅是 ID,例如:

input DeleteUserInput {
  id: ID!
  deleteOwnAssignments: Boolean
  deleteManagedAssignments: Boolean
}

mutation deleteUser($input: DeleteUserInput!) {
  deleteUser(input: $input)
}

boolean deleteUser(String id, boolean deleteOwnAssignments, boolean deleteManagedAssignments) {
   if (deleteOwnAssignments) {
       dataBase.execute("DELETE FROM Assignment WHERE assigned_to = :id");
   }
   if (deleteManagedAssignments) {
       dataBase.execute("DELETE FROM Assignment WHERE manager_id = :id");
   }
   dataBase.execute("DELETE FROM User WHERE id = :id");
   return true; //or return whatever is appropriate
}

【讨论】:

    猜你喜欢
    • 2018-11-02
    • 1970-01-01
    • 2015-04-24
    • 2011-06-29
    • 2010-10-05
    • 2011-09-21
    • 2012-02-27
    • 2012-09-08
    • 2012-09-07
    相关资源
    最近更新 更多