Prisma+Apollo Server/Nexus冗余User查询的原因及解决方法
问题原因
你在articles和posts的resolve函数中使用了ctx.prisma.user.findUnique({ where: { id: parent.id } }).articles()这类链式调用,这会触发两次冗余操作:
- 首先执行
findUnique查询用户(也就是那些重复出现的SELECT "public"."User"."id"语句) - 再基于查询到的用户实例去获取关联的文章/帖子
由于你的GraphQL查询同时请求了articles和posts两个字段,每个字段的resolve逻辑都会重复执行上述步骤,因此出现了多次不必要的用户ID查询。
解决方法
方法1:直接查询关联模型,跳过用户查询
既然parent对象已经包含了用户的id,完全不需要先查询用户,直接针对关联表进行过滤查询即可:
const User = objectType({ name: 'User', definition(t) { t.nonNull.id('id'); t.nonNull.string('name'); t.list.field('articles', { type: nonNull(Article), async resolve(parent, _args, ctx) { return ctx.prisma.article.findMany({ where: { userId: parent.id } }); }, }); t.list.field('posts', { type: nonNull(Post), async resolve(parent, _args, ctx) { return ctx.prisma.post.findMany({ where: { userId: parent.id } }); }, }); }, });
方法2:顶层查询预加载关联(更优方案,彻底解决N+1)
在users查询的resolve函数中,使用Prisma的include语法一次性预加载用户的articles和posts关联数据,这样Nexus会自动将预加载的数据映射到User类型对应的字段,无需再单独编写resolve逻辑:
首先修改users查询的resolver:
const Query = objectType({ name: 'Query', definition(t) { t.list.field('users', { type: nonNull(User), args: { names: nonNull(list(nonNull(string()))) }, async resolve(_, args, ctx) { return ctx.prisma.user.findMany({ where: { name: { in: args.names } }, include: { articles: true, posts: true, }, }); }, }); }, });
然后简化User类型的定义,去掉自定义resolve:
const User = objectType({ name: 'User', definition(t) { t.nonNull.id('id'); t.nonNull.string('name'); t.list.field('articles', { type: nonNull(Article) }); t.list.field('posts', { type: nonNull(Post) }); }, });
这种方式不仅能消除冗余的用户查询,还会将所有关联查询合并为高效的批量查询,从根源上避免N+1问题。
内容的提问来源于stack exchange,提问作者Naor
相关产品推荐
相关产品推荐

