You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Nexus/Prisma正确定义显式多对多关系?方案对比

关于Nexus中Prisma显式多对多关系解析器的方案对比

我是Prisma、Nexus及GraphQL的新手,在Nexus中定义Prisma显式多对多关系的字段解析器时遇到了不小的困难,最终找到了两种可行的实现方案,后来又补充了第三种。想知道哪种方案更优、原因是什么,以及哪里能找到相关性能和优劣的文档。

相关模型定义(schema.prisma)

model User {
  id        Int               @id @default(autoincrement())
  email     String            @unique
  password  String
  groups    UsersOnGroups[]
  recipes   Recipe[]
  lists     List[]
}

model Group {
  id      Int                 @id @default(autoincrement())
  name    String   
  users   UsersOnGroups[]
  recipes Recipe[]
  lists   List[]
}

model UsersOnGroups {
  user User @relation(fields: [userId], references: [id])
  userId Int 
  group Group @relation(fields: [groupId], references: [id])
  groupId Int

  @@id([userId, groupId])
}

仅展示User模型,Group模型实现逻辑基本一致,为简洁起见省略了部分字段。

三种实现方案

方案一:直接查询中间表

export const User = objectType({
  name: 'User',
  definition(t) {
    t.nonNull.int('id')
    t.string('email')
    t.nonNull.list.nonNull.field('groups', {
      type: Group,
      resolve: async (parent, _args, context) => {
        const groups = await context.prisma.usersOnGroups.findMany({
          where: {
            userId: parent.id,
          },
          include: {
            group: true,
          },
        })

        return groups.map((groupRelation) => groupRelation.group)
      },
    })
  },
})

方案二:反向查询Group模型

export const User = objectType({
  name: 'User',
  definition(t) {
    t.nonNull.int('id')
    t.string('email')
    t.nonNull.list.nonNull.field('groups', {
      type: Group,
      resolve: (parent, _args, context) => {
        return context.prisma.group.findMany({
          where: {
            users: {
              every: {
                userId: parent.id,
              },
            },
          },
        })
      },
    })
  },
})

补充方案:通过User模型关联查询中间表

t.nonNull.list.nonNull.field('groups', {
      type: Group,
      resolve: async (parent, _args, context: Context) => {
        const groups = await context.prisma.user
          .findUnique({
            where: {
              id: parent.id,
            },
          })
          .groups({
            include: {
              group: true,
            },
          })

        return groups?.map((groupRelation) => groupRelation.group) ?? []
      },
    })

方案优劣对比与结论

各方案分析

  • 方案一:逻辑直观,直接操作中间表关联Group,生成的SQL是单表JOIN查询,利用中间表的索引能保证高效查询。缺点是需要手动处理结果映射,代码稍显繁琐。
  • 方案二:语义不直观,反向查询Group的写法不符合"查询用户所属组"的业务逻辑;every条件生成的SQL可能存在性能隐患,当Group数据量大时,过滤效率远低于直接从中间表查询。
  • 补充方案:贴合Prisma关联查询的设计理念,代码更简洁;生成的SQL与方案一类似,性能相当,同时规避了手动映射的繁琐,是更符合Prisma最佳实践的写法。

最优选择

优先使用补充方案,其次是方案一;方案二因语义不直观和潜在性能问题,不推荐使用。

相关文档参考

你可以在Prisma官方文档的这些板块找到对应内容:

  • 显式多对多关系章节:讲解模型设计和基础查询方式;
  • 性能优化板块:包含索引优化、查询效率分析的内容;
  • 关联查询指南:对比不同关联查询写法的差异及生成的SQL逻辑。

内容的提问来源于stack exchange,提问作者theiereman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 01:11:02