У меня есть вопрос о запросе реляционных данных между 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);
}
}
});
}
}
это хороший вариант? или есть другой способ оптимизировать вызов базы данных?