【问题标题】:How can I use the fields in a GraphQL query to perform nested reads with Prisma?如何使用 GraphQL 查询中的字段通过 Prisma 执行嵌套读取?
【发布时间】:2022-06-14 20:33:09
【问题描述】:

我正在使用 Prisma 来实现 GraphQL 接口,以公开存储在 PostgreSQL 数据库中的一些数据。我的代码受到GraphQL Tools (SDL-first) example 的启发。不过这个逻辑效率很低,我想改进一下。

这是显示问题并寻求解决方案的最少代码段。我的真实代码当然更复杂。

我的 GraphQL 架构
type Query {
  allUsers: [User!]!
}
type User {
  name: String!
  posts: [Post!]!
}
type Post {
  text: String!
  author: User!
}
我的解析器对象,在 Node.JS 代码中
const resolvers = {
  Query: {
    allUsers: ()=>prisma.users.findMany()
  },
  User: {
    posts: (user)=>prisma.posts.findMany({where:{author:user.id}})
  }
};
问题

此代码有效,但效率低下。假设您正在运行查询{allUsers{posts{text}}}

  1. 我的代码对 PostgreSQL 运行 N+1 个查询来获取整个结果:一个来获取用户列表,然后另一个 N:每个用户一个。使用 JOIN 的单个查询就足够了。

  2. 我的代码从它查询的每个表中选择每一列,即使我只需要 user.id 而不需要 user.name 或其他任何东西。

问题

我知道 Prisma 支持 nested searches (include and select options) 可以解决这两个问题。但是我不知道如何使用 GraphQL 查询配置选项对象。

如何从 GraphQL 查询中提取请求的字段列表?以及如何使用这些来创建选项对象以使用 Prisma 执行最佳嵌套搜索?

【问题讨论】:

    标签: node.js graphql prisma


    【解决方案1】:

    这个包可以帮你解析请求信息:https://www.npmjs.com/package/graphql-parse-resolve-info

    然后您需要将其转换为可以在 ORM 中使用的可用参数。

    这里是 NestJS 的一个例子:

    import {createParamDecorator, ExecutionContext} from '@nestjs/common';
    import {GqlExecutionContext} from '@nestjs/graphql';
    import {GraphQLResolveInfo} from 'graphql';
    import {parseResolveInfo, ResolveTree} from 'graphql-parse-resolve-info';
    
    export type PrismaSelect = {
      select: {
        [key: string]: true | PrismaSelect;
      };
    };
    
    export const Relations = createParamDecorator(
      (data: unknown, ctx: ExecutionContext) => {
        const info = GqlExecutionContext.create(ctx).getInfo<GraphQLResolveInfo>();
        const ast = parseResolveInfo(info);
        return astToPrisma(Object.values((ast as ResolveTree).fieldsByTypeName)[0]);
      },
    );
    
    export const astToPrisma = (ast: {
      [str: string]: ResolveTree;
    }): PrismaSelect => {
      return {
        select: Object.fromEntries(
          Object.values(ast).map(field => [
            field.name,
            Object.keys(field.fieldsByTypeName).length === 0
              ? true
              : astToPrisma(Object.values(field.fieldsByTypeName)[0]),
          ]),
        ),
      };
    };
    

    然后你做:

    import {Parent, Query, ResolveField, Resolver} from '@nestjs/graphql';
    import {PrismaService} from '../services/prisma.service';
    import {User} from '../entities/user.entity';
    import {Relations} from 'src/decorators/relations.decorator';
    import {Prisma} from '@prisma/client';
    
    @Resolver(() => User)
    export class UserResolver {
      constructor(public prisma: PrismaService) {}
      @Query(() => [User])
      async usersWithRelationsResolver(
        @Relations() relations: {select: Prisma.UserSelect},
      ): Promise<Partial<User>[]> {
        return this.prisma.user.findMany({
          ...relations,
        });
      }
    

    或者,如果你想解决 N+1 问题,你可以使用 Prisma 内置的findUnique 方法。见https://www.prisma.io/docs/guides/performance-and-optimization/query-optimization-performance#solving-the-n1-problem

    【讨论】:

      猜你喜欢
      • 2022-12-14
      • 1970-01-01
      • 2019-09-20
      • 1970-01-01
      • 2020-12-31
      • 2019-03-23
      • 2019-08-12
      • 2019-07-08
      • 2017-07-17
      相关资源
      最近更新 更多