我对 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);
}
}
});
}
}
这是一个不错的选择吗?还是有其他方法可以优化数据库调用?