中继突变:突变分页关联
Relay Mutations: Mutating Paginated Associations
在很多情况下,我们需要更改一对多或多对多关联,并且关联以分页列表的形式暴露给查询。
有一些关键要求:
- 客户端必须能够删除、添加和更新关联的元素
- 在某些情况下,关联的顺序很重要,在这种情况下,客户端也必须能够对元素重新排序
不太重要:
- 客户应该能够在创建父代时指定关联(例如,在创建产品的同时创建一组有序的变体)
- 客户端应该能够通过某种形式的事务原子性立即删除、添加、更新和重新排序关联的元素
- 客户不必为了添加或删除单个元素而对整个当前关联进行分页
这个问题有多种可能的解决方案:
选项 1 - 单一输入字段,无额外突变
输入类型有一个数组输入字段,表示关联的全部真实性(添加新元素,更新现有元素,删除缺失元素,并在必要时保留顺序)。
缺点:删除非常含蓄。客户端必须对关联的整个当前状态进行分页。不是颗粒状的。
选项 2 - 带位置的单输入字段,删除变异
输入类型有一个数组输入字段,用于更新现有元素和添加新元素(忽略缺少的元素)。可以在元素上指定位置或索引值以对它们重新排序。一个单独的突变用于删除元素。
缺点:删除在突变中自行关闭而所有其他操作都在父级上是不一致的。不是很细化。
选项 3 - 单一输入字段,删除和重新排序突变
输入类型有一个数组输入字段,用于更新现有元素和添加新元素(忽略缺少的元素)。单独的突变用于删除和重新排序元素。
缺点:客户无法将新元素添加到关联中的特定位置,它们必须单独添加然后重新排序。不是很细化。
选项 4 - 单输入字段,Add/Delete/Reorder 突变
与选项 3 类似,但输入字段仅用于更新;一个单独的突变用于添加新元素。
缺点:客户端必须进行多次更改才能执行复杂的更新。客户端无法创建具有初始关联的父项。
选项 5 - 完全独立的突变
父输入类型没有相关字段,一切都是通过 add/remove/update/reorder.
的四个单独突变完成的
优点:非常明确和细化,将不同的数据模型对象分开。
缺点:客户端必须进行多次更改才能执行复杂的更新。客户端无法创建具有初始关联的父项。
选项 6 - 两个带位置的输入字段
输入类型有两个数组字段:一个用于更新、添加和重新排序元素(参见选项 2),另一个用于删除。
缺点:感觉我们正在污染父突变;不是颗粒状的。
选项 7 - 两个输入字段,重新排序突变
与选项 6 一样,除了使用单独的重新排序突变而不是位置参数。
缺点:不一致的是重新订购会自行关闭。另请参阅选项 6 的缺点。
所有这些选项似乎都有缺点。选项 5 似乎是最明确的,但要求用户同时使用多个突变,其中操作不再是真正的原子操作。
Facebook 是如何处理这些类型的突变的?你的方法是什么?谢谢!
所有这些方法之间肯定存在权衡取舍,因此选择一个真正取决于我尝试构建的内容。
Facebook 模式中最常见的情况有两个很好的约束:
- 没有重新排序
- 无批量操作
Facebook 上的评论就是典型的例子。对于这种情况,我们已经成功地使用了选项 5,我可以自信地推荐它。对于评论,我们只有三个突变; commentCreate
、commentEdit
和 commentDelete
。
对于这样的情况,我们得出了常见的模式:
- 对于
create
和edit
(或update
)突变,突变解析到的对象通常包含边已修改(这里我使用边缘的意义与我们在 Pagination Best Practice doc 中使用边缘的意义相同)。然后,您可以从该边缘轻松获取修改后的对象……但您也可以获得可能需要的任何边缘数据。
delete
突变通常只是 return 被删除对象的 ID;如果客户端在输入中提供了该 ID,这对客户端来说纯粹是方便,但如果删除输入需要一些其他信息并且服务器将其转换为 ID,那么这将非常有用,在这种情况下 returning已删除的 ID 允许客户端知道删除了哪个对象。
Option 5 [...] Clients cannot create the parent with initial associations.
我不太确定我是否遵循了这一点;我的评论示例在这里没有很好的例子(因为它不存在于产品中),但假设如果我想 post 评论 和 回复该评论同时,我可以想象做这样的事情:
commentCreate(input: {text:"Hello World", replies:[{text:"Reply 1"}, {text:"Reply 2"}]})
我正在重用 commentCreate
用作 replies
接受的复数输入类型的输入类型。
对于需要重新排序或批量操作的情况,这听起来像是您正在查看的主要情况,我没有很好的答案,而且这绝对是您所说的更棘手的情况。我认为我没有看到足够多的例子来自信地推荐一个选项而不是另一个选项,而且我认为哪个选项最好可能最终取决于特定的用例。不过,要考虑的另一个选择是在输入是操作列表的情况下进行单一突变。因此,如果我们有一个 "favorite photos" 的列表并且我们想要进行批量更新,我们可以这样做:
favoritePhotosUpdate({operations: {operation:ADD, addedId:1234}, {operation:REMOVE, addedId:5678}, {operation:UPDATE, oldId:4321, newId:8765}, {operation:SWAP, oldId:32, newId:76}
不确定这是否真的比上面的选项更好,但这是我们过去讨论过的另一个选项(虽然我不确定我们是否已经付诸实践),所以它至少值得添加到列表中。
希望对您有所帮助!
在很多情况下,我们需要更改一对多或多对多关联,并且关联以分页列表的形式暴露给查询。
有一些关键要求:
- 客户端必须能够删除、添加和更新关联的元素
- 在某些情况下,关联的顺序很重要,在这种情况下,客户端也必须能够对元素重新排序
不太重要:
- 客户应该能够在创建父代时指定关联(例如,在创建产品的同时创建一组有序的变体)
- 客户端应该能够通过某种形式的事务原子性立即删除、添加、更新和重新排序关联的元素
- 客户不必为了添加或删除单个元素而对整个当前关联进行分页
这个问题有多种可能的解决方案:
选项 1 - 单一输入字段,无额外突变
输入类型有一个数组输入字段,表示关联的全部真实性(添加新元素,更新现有元素,删除缺失元素,并在必要时保留顺序)。
缺点:删除非常含蓄。客户端必须对关联的整个当前状态进行分页。不是颗粒状的。
选项 2 - 带位置的单输入字段,删除变异
输入类型有一个数组输入字段,用于更新现有元素和添加新元素(忽略缺少的元素)。可以在元素上指定位置或索引值以对它们重新排序。一个单独的突变用于删除元素。
缺点:删除在突变中自行关闭而所有其他操作都在父级上是不一致的。不是很细化。
选项 3 - 单一输入字段,删除和重新排序突变
输入类型有一个数组输入字段,用于更新现有元素和添加新元素(忽略缺少的元素)。单独的突变用于删除和重新排序元素。
缺点:客户无法将新元素添加到关联中的特定位置,它们必须单独添加然后重新排序。不是很细化。
选项 4 - 单输入字段,Add/Delete/Reorder 突变
与选项 3 类似,但输入字段仅用于更新;一个单独的突变用于添加新元素。
缺点:客户端必须进行多次更改才能执行复杂的更新。客户端无法创建具有初始关联的父项。
选项 5 - 完全独立的突变
父输入类型没有相关字段,一切都是通过 add/remove/update/reorder.
的四个单独突变完成的优点:非常明确和细化,将不同的数据模型对象分开。 缺点:客户端必须进行多次更改才能执行复杂的更新。客户端无法创建具有初始关联的父项。
选项 6 - 两个带位置的输入字段
输入类型有两个数组字段:一个用于更新、添加和重新排序元素(参见选项 2),另一个用于删除。
缺点:感觉我们正在污染父突变;不是颗粒状的。
选项 7 - 两个输入字段,重新排序突变
与选项 6 一样,除了使用单独的重新排序突变而不是位置参数。
缺点:不一致的是重新订购会自行关闭。另请参阅选项 6 的缺点。
所有这些选项似乎都有缺点。选项 5 似乎是最明确的,但要求用户同时使用多个突变,其中操作不再是真正的原子操作。
Facebook 是如何处理这些类型的突变的?你的方法是什么?谢谢!
所有这些方法之间肯定存在权衡取舍,因此选择一个真正取决于我尝试构建的内容。
Facebook 模式中最常见的情况有两个很好的约束:
- 没有重新排序
- 无批量操作
Facebook 上的评论就是典型的例子。对于这种情况,我们已经成功地使用了选项 5,我可以自信地推荐它。对于评论,我们只有三个突变; commentCreate
、commentEdit
和 commentDelete
。
对于这样的情况,我们得出了常见的模式:
- 对于
create
和edit
(或update
)突变,突变解析到的对象通常包含边已修改(这里我使用边缘的意义与我们在 Pagination Best Practice doc 中使用边缘的意义相同)。然后,您可以从该边缘轻松获取修改后的对象……但您也可以获得可能需要的任何边缘数据。 delete
突变通常只是 return 被删除对象的 ID;如果客户端在输入中提供了该 ID,这对客户端来说纯粹是方便,但如果删除输入需要一些其他信息并且服务器将其转换为 ID,那么这将非常有用,在这种情况下 returning已删除的 ID 允许客户端知道删除了哪个对象。
Option 5 [...] Clients cannot create the parent with initial associations.
我不太确定我是否遵循了这一点;我的评论示例在这里没有很好的例子(因为它不存在于产品中),但假设如果我想 post 评论 和 回复该评论同时,我可以想象做这样的事情:
commentCreate(input: {text:"Hello World", replies:[{text:"Reply 1"}, {text:"Reply 2"}]})
我正在重用 commentCreate
用作 replies
接受的复数输入类型的输入类型。
对于需要重新排序或批量操作的情况,这听起来像是您正在查看的主要情况,我没有很好的答案,而且这绝对是您所说的更棘手的情况。我认为我没有看到足够多的例子来自信地推荐一个选项而不是另一个选项,而且我认为哪个选项最好可能最终取决于特定的用例。不过,要考虑的另一个选择是在输入是操作列表的情况下进行单一突变。因此,如果我们有一个 "favorite photos" 的列表并且我们想要进行批量更新,我们可以这样做:
favoritePhotosUpdate({operations: {operation:ADD, addedId:1234}, {operation:REMOVE, addedId:5678}, {operation:UPDATE, oldId:4321, newId:8765}, {operation:SWAP, oldId:32, newId:76}
不确定这是否真的比上面的选项更好,但这是我们过去讨论过的另一个选项(虽然我不确定我们是否已经付诸实践),所以它至少值得添加到列表中。
希望对您有所帮助!