Лучший способ обрабатывать один-ко-многим с помощью type-graphql typeorm и dataloader

Я пытаюсь выяснить, как лучше всего обрабатывать отношения «один ко многим», используя type-graphql и typeorm с помощью postgresql db (используя сервер apollo с экспрессом). У меня есть таблица пользователей, которая имеет отношение «один ко многим» с таблицей курсов. В настоящее время я обрабатываю это с помощью поля @RelationId для создания столбца userCourseIds, а затем с помощью @FieldResolver с загрузчиком данных для пакетной выборки курсов, принадлежащих этому пользователю (-ам). Моя проблема в том, что с полем @RelationId делается отдельный запрос для получения идентификаторов отношений, независимо от того, действительно ли я запрашиваю userCourses. Есть ли способ справиться с этим, когда он не будет делать этот дополнительный запрос, или есть лучший способ справиться с отношениями «один ко многим»?

Пользовательская сторона отношения:

@OneToMany(() => Course, (course) => course.creator, { cascade: true })
userCourses: Course[];
@RelationId((user: User) => user.userCourses)
userCourseIds: string;

Курсовая сторона отношения:

@Field()
@Column()
creatorId: string;

@ManyToOne(() => User, (user) => user.userCourses)
creator: User;

userCourses FieldResolver:

@FieldResolver(() => [Course], { nullable: true })
async userCourses(@Root() user: User, @Ctx() { courseLoader }: MyContext) {
  const userCourses = await courseLoader.loadMany(user.userCourseIds);
  return userCourses;
}

person Joel Jacobsen    schedule 30.08.2020    source источник


Ответы (1)


Поле userCourses можно записать, как показано ниже, с использованием @TypeormLoader декоратора, предоставляемого type-graphql-datalaoader.

@ObjectType()
@Entity()
class User {
  ...

  @Field((type) => [Course])
  @OneToMany(() => Course, (course) => course.creator, { cascade: true })
  @TypeormLoader((course: Course) => course.creatorId, { selfKey: true })
  userCourses: Course[];
}

@ObjectType()
@Entity()
class Course {
  ...

  @ManyToOne(() => User, (user) => user.userCourses)
  creator: User;

  @RelationId((course: Course) => course.creator)
  creatorId: string;
}

Дополнительные запросы больше не будут выдаваться, поскольку userCourseIds опущено. Хотя creatorId с @RelationId существует, он не будет выдавать дополнительных запросов, поскольку объект имеет собственное значение.

Обновление: июнь 2021 г.

@TypeormLoader больше не нуждается в аргументах. Поэтому @RelationId при желании можно полностью опустить.

person Kazuaki Tanida    schedule 10.11.2020
comment
@RelationId отправляет мне дополнительные запросы. - person NeverBe; 10.11.2020
comment
Принадлежит ли свойство отношения сущности? @ManyToOne и @OneToOne с @JoinColumn владеют идентификатором. @RelationId для них не вызовет лишних запросов. - person Kazuaki Tanida; 10.11.2020
comment
как загрузчик данных GraphQL работает с @RelationId? Думаю, это игнорируется. Гарантированный выпуск N + 1 - person NeverBe; 10.11.2020
comment
В приведенном выше случае creatorId не будет инициировать дополнительные запросы, поскольку он предназначен для @ManyToOne защиты. @TypeormLoader из userCourses выбирает все course, которые имеют идентификатор user внутри его batchLoadFn под капотом. - person Kazuaki Tanida; 10.11.2020
comment
course.creatorId необходим для @TypeormLoader, чтобы он мог знать условие запроса на выборку. - person Kazuaki Tanida; 10.11.2020