Лучший способ обработать данные отношений на подфильтре с помощью type-graphql - PullRequest
0 голосов
/ 25 мая 2019

У меня есть вопрос о запросе реляционных данных между graphql / typeorm. Лучший способ объяснить это - привести пример:)

Для моего вопроса я использую, например, Prisma API (https://www.graphqlbin.com/v2/6RQ6TM) Этот API является примером StarWars GraphQL API

Если я хочу получить все фильмы и связанные с ними персонажи, я делаю запрос следующим образом:

ДЕЛО A

 {
   allFilms{
    title
    characters{
        name
    }
   }
 }

в этом случае вызов реляционной базы данных можно сделать одним кадром, извлечь фильм и персонажа вместе (sql select с join) Мой вопрос, как сохранить производительность SQL при добавлении фильтра в запрос, как это

ДЕЛО B

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

В этом случае кажется, что мы должны сделать это с FieldResolver и выполнить другой вызов базы данных для извлечения дочернего элемента

Для уточнения см. Примеры ниже:

СЛУЧАЙ A (ПРОСТАЯ РЕАЛИЗАЦИЯ) в случае с backjnd-версией nodejs в машинописном тексте и ORM-типе мы могли бы это примерно так: Организация:

    @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 (ДРУГОЕ РАСШИРЕННОЕ ОСУЩЕСТВЛЕНИЕ) Моя цель - получить расширенную информацию о запросе graphql, определить отношение в запросе и создать SQL-запрос с помощью join

 @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);        
          }
        }        
      });
  }
}

это хороший вариант? или есть другой способ оптимизировать вызов базы данных?

...