繁体   English   中英

如何使用 GraphQL 查询中的字段通过 Prisma 执行嵌套读取?

[英]How can I use the fields in a GraphQL query to perform nested reads with Prisma?

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

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

我的 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 支持嵌套搜索( includeselect选项) ,这可以解决这两个问题。 但是我不知道如何使用 GraphQL 查询配置选项对象。

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

这个包可以帮助你解析请求信息: 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM