如何在用户连接请求的多对多表中避免重复行并适配Hasura的on_conflict参数
最优解决方案:PostgreSQL表达式唯一约束 + Hasura Upsert
这是双向邀请场景里的典型问题,核心是要让「A邀请B」和「B邀请A」被视为同一组关系,从而避免重复插入并自动合并状态。下面是最常用且适配Hasura的实现方案:
1. 在PostgreSQL中添加表达式唯一约束
不需要新增额外字段,直接利用PostgreSQL的least()和greatest()函数,对sender_id和receiver_id进行排序后创建唯一约束。这样不管双方谁作为发送方/接收方,排序后的组合都是唯一的,从数据库层面杜绝重复行。
执行以下SQL语句(可通过Hasura的SQL编辑器或直接连接Postgres执行):
ALTER TABLE invitations ADD CONSTRAINT unique_invitation_pair UNIQUE (least(sender_id, receiver_id), greatest(sender_id, receiver_id));
这个约束会确保:对于任意两个用户,不管邀请方向如何,只会存在一组对应的记录。
2. 刷新Hasura元数据
在Hasura控制台中,进入Data -> 你的表(invitations),点击右上角的「Refresh」按钮,让Hasura识别新添加的唯一约束。
3. 编写Hasura Upsert Mutation
现在可以利用Hasura的on_conflict参数,指定我们刚创建的unique_invitation_pair约束作为冲突判断条件。当检测到冲突时(即双方互相发送了邀请),自动将状态更新为connected。
示例Mutation:
mutation UpsertInvitation { insert_invitations( objects: [{sender_id: "1234", receiver_id: "6789", status: "pending"}], on_conflict: { constraint: unique_invitation_pair, update_columns: [status], _set: {status: "connected"} } ) { returning { id sender_id receiver_id status } } }
逻辑说明:
- 当插入的邀请是全新的(没有对应的反向邀请),则正常插入一条
pending状态的记录。 - 当插入的邀请已经存在反向记录(比如用户6789已经给1234发过邀请),则触发冲突逻辑,将现有记录的
status更新为connected,完美解决双方同时发邀请的场景。
为什么这个方案最优?
- 无应用层侵入:不需要在代码里对UUID排序后再插入,数据库自动处理唯一性逻辑,降低应用层复杂度。
- Hasura原生支持:Hasura完全支持将表达式创建的唯一约束作为
on_conflict的目标,不需要额外配置。 - 性能高效:直接利用数据库的唯一索引,冲突判断和更新都是原子操作,避免竞态条件。
内容的提问来源于stack exchange,提问作者Stuiterbal
相关产品推荐
相关产品推荐

