GraphQL mutation是否无需先查询ID即可关联已有记录?
我是一名业余开发者,正在学习GraphQL(有基础SQL知识,曾用SQLite搭建家庭应用,完成过数据增删改查和关联操作)。用Hasura构建数据库表关系模式的体验很棒,查询变得特别简单。不过在学习GraphQL数据变更(mutation)时,有个疑问想弄明白:
我要插入一条用户(John Brown)的记录,这个用户自2003年起就职于ibm公司,用户和公司分属不同表。当前用的mutation语句是:
mutation MyMutation { insert_users(objects: { user_company: { data: { name: "ibm", date: 2003 } }, first_name: "John", last_name: "Brown" }) { returning { id } } }
这条语句能正常执行,但每次都会创建新的John Brown用户和ibm公司记录。我的问题是:
- GraphQL是否要求必须先查询目标记录的ID(类似SQL操作)?
- 是否存在GraphQL机制可实现「若指定数据的对象已存在,则复用该对象建立关联而非创建新对象」?
问题解答
1. GraphQL是否必须先查询目标ID?
GraphQL本身没有强制要求必须先查目标记录ID,这取决于你用的服务端实现(比如你用的Hasura)。你当前用的user_company: { data: {...} }是Hasura的创建关联对象语法,默认会生成新的公司记录。如果要关联已有公司,确实需要先拿到公司的唯一标识(比如ID),然后用Hasura的关联已有对象语法——用connect字段替代data字段。
比如先查询ibm公司的ID:
query GetCompanyId { companies(where: {name: {_eq: "ibm"}, date: {_eq: 2003}}) { id } }
再用查到的ID关联插入用户:
mutation InsertUser { insert_users(objects: { first_name: "John", last_name: "Brown", user_company: { connect: { id: 123 } } # 替换为实际查询到的ID }) { returning { id } } }
这本质和SQL里先查ID再关联的逻辑一致,但不是GraphQL的通用规则,只是Hasura这类工具的具体实现方式。
2. 是否存在复用已有对象的机制?
当然有,在Hasura里可以通过**upsert(插入或更新)**结合关联的upsert语法实现,不用手动拆分查询和插入步骤。
首先需要给companies表的name和date字段设置联合唯一约束(因为这两个字段的组合能唯一标识公司记录),然后在mutation里使用on_conflict参数:
mutation InsertUserWithCompanyUpsert { insert_users(objects: { first_name: "John", last_name: "Brown", user_company: { data: { name: "ibm", date: 2003 }, on_conflict: { constraint: companies_name_date_key, # 替换为你设置的唯一约束名称 update_columns: [] # 冲突时不更新任何字段,直接复用已有记录 } } }) { returning { id user_company { id name } } } }
这个mutation的逻辑是:如果name="ibm"且date=2003的公司记录已存在,就直接关联该记录;如果不存在,就创建新的公司记录再关联。
如果还要避免重复创建John Brown用户,同样给users表的first_name和last_name设置联合唯一约束,再加上用户自身的on_conflict处理:
mutation UpsertUserWithCompany { insert_users(objects: { first_name: "John", last_name: "Brown", user_company: { data: { name: "ibm", date: 2003 }, on_conflict: { constraint: companies_name_date_key, update_columns: [] } } }, on_conflict: { constraint: users_first_name_last_name_key, update_columns: [user_company_id] # 冲突时可选择更新关联的公司ID }) { returning { id user_company { id name } } } }
这种机制和SQL里的INSERT ... ON CONFLICT逻辑完全对应,Hasura把它封装成了GraphQL语法,简化了操作流程。
内容的提问来源于stack exchange,提问作者WoJ

