首页 > 解决方案 > 使用 type-graphql 处理子过滤器上的关系数据的最佳方法

问题描述

我对 graphql / typeorm 之间的关系数据查询有疑问。最好的解释方法是举个例子:)

出于我的问题的目的,例如我使用 prisma API ( https://www.graphqlbin.com/v2/6RQ6TM ) 此 API 是 StarWars GraphQL API 的一个示例

如果我想获取所有电影和相关角色,我会这样查询:

案例一

 {
   allFilms{
    title
    characters{
        name
    }
   }
 }

在这种情况下,可以一次性完成关系数据库调用,同时检索电影和角色(sql select with join) 我的问题是当在这样的查询中添加过滤器时如何保持 sql 性能

案例二

{
   allFilms{
    title
    characters(filter: {name_contains: "PO"}){
        name
    }
   }
 }

在这种情况下,我们似乎必须使用 FieldResolver 执行此操作并执行其他数据库调用来检索子

为了澄清,请参阅以下示例:

案例 A(简单实现)在 typescript 和 typeorm ORM 中的 nodejs 后端的情况下,我们可以这样:实体:

    @Entity()
    @ObjectType()
    export class Film { 

        @PrimaryColumn()
        @Field(type => ID, { description: 'The id of the object.'})
        id: string;

        @Column()
        @Field({ description: 'The title of this film'})
        title: string;

        .....

        @ManyToMany(type => Person, person => person.films )
        @JoinTable({ name: 'film_person' })   
        @Field(type => [Person], { nullable: true }) 
        characters?: Person[];
    }

解析器:

    @Resolver(of => Film)
    export class FilmResolvers { 

      constructor(private readonly filmService: FilmService) {}

      @Query(returns => [Film])  
      async allFilms(@Args() args: QueryAllFilmDTO): Promise<Film[]> {    
        return this.filmService.findAll(args);
      }
    }

案例 B(扩展实施)

解析器:

 @Resolver(of => Film)
 export class FilmResolvers {

  constructor(
    private readonly filmService: FilmService,    
    @InjectRepository(Person) private readonly personRepository: Repository<Person>,
    ) {}

  @ResolveProperty(type => [Person])  
  async characters(@Parent() film: Film, @Args() args: PersonQueryAllDTO) {

    // Low Performance in this case 
    const persons = await this.personRepository
      .createQueryBuilder('person')
      .leftJoin('person.films', 'film','film.id = :films_param',{films_param: [film.id]})      
      .getMany();

    return persons;
  }

  @Query(returns => [Film])  
  async allFilms(@Args() args: QueryAllFilmDTO): Promise<Film[]> {
    this.detectRelation(info.operation.selectionSet);        
    return this.filmService.findAll(args);
  }
}

我们进行手动连接(两个数据库调用)的问题我正在考虑其他解决方案,但我需要您的审查:

案例B(其他扩展实现)我的目标是检索extendend graphql请求信息,检测查询中的关系并使用join构造sql查询

 @Resolver(of => Film)
 export class FilmResolvers { 

  constructor(
    private readonly filmService: FilmService,  
    @InjectRepository(Person) private readonly personRepository: Repository<Person>,
    ) {}


    @Query(returns => [Film])  
  async allFilms(@Args() args: QueryAllFilmDTO, @Info() info: GraphQLResolveInfo): Promise<Film[]> {

    const relations: string[] = [];
    this.getRelations(info.operation.selectionSet, (relation) =>{
      relations.push(relation);
    });

    return this.filmService.findAll(args, relations);
  }




  private getRelations(selectionSetNode: SelectionSetNode, callback: relationCallback, level=0){      
      selectionSetNode.selections.forEach( (subSelection) => {                
        if(subSelection.kind === 'Field'){
          const fieldNode = subSelection as FieldNode;          
          if(fieldNode.selectionSet){
            if(level)              
              callback(fieldNode.name.value);            
            this.getRelations(fieldNode.selectionSet, callback, ++level);        
          }
        }        
      });
  }
}

这是一个不错的选择吗?还是有其他方法可以优化数据库调用?

标签: graphqltypeormtypegraphql

解决方案


推荐阅读