我有一个关于模式/查询设计的问题。
假设有一个Study模型。
Study可以有多个接班人,所以有明显的parentId字段。
case class Study(id: StudyId, name: String, parentId: Option[StudyId])上下文中的User可以访问学习,因此我们希望将所有可访问的研究作为树返回到UI。因此,查询字段可以如下所示
Field(name = "accessibleStudies",
fieldType = ListType(StudyNode.Type),
resolve = implicit ctx => inject[StudyQueries].accessibleStudies(ctx))在accessibleStudies方法中,我们要求DB提供数据,并准备可以作为树在UI上查看的列表。
class StudyQueries {
def accessibleStudies(ctx: Ctx): Future[Seq[StudyNode]] = {
// 50 lines of data fetching and transformation
}
}现在有趣的部分。StudyNode应该有额外的字段- progress,它可以被计算为所有继承者的进展的递归和,因此我们需要每次都要有完整的可访问的研究列表来获得单个节点的progress。所以简单的方法就像
case class StudyNode(entity: Study)
object StudyNode {
val Type = ObjectType[Ctx, StudyNode](
...
Field(name = "progress",
fieldType = ProgressType,
resolve = implicit ctx => inject[ProgressService].progressOfStudy(...))
)
}将导致巨大的开销,因为我们需要对每个节点进行一次又一次的可访问性研究。然而,在StudyQueries.accessibleStudies内部进行calc似乎很方便,所以我们将有一些类似准备好的Map[EntityId, Progress]和StudyNode定义,现在可以这样修改。
case class StudyNode(entity: Study,
progresses: Map[EntityId, Progress])
object StudyNode {
val Type = ObjectType[Ctx, StudyNode](
...
Field(name = "progress",
fieldType = ProgressType,
resolve = implicit ctx => progresses(ctx.value.entity.getId))
)
}但对我来说这不像是一个干净的解决方案。此外,我还必须手动检查ctx.astFields,以验证是否正在调用progress字段。现在我想知道是否有更好的方法来处理这种情况。
发布于 2018-07-03 09:47:58
您可以使用Deferred Resolvers / Fetchers,它可以在查询执行过程中缓存相同ids的结果,因此不需要询问数据库两次。
更多关于这一点的信息:
https://sangria-graphql.org/learn/#high-level-fetch-api
此外,您还可以使用任何第三方库来进行更有效的获取,例如
http://47deg.github.io/fetch/或
发布于 2018-07-11 03:46:20
我相信您正在尝试避免N+1查询。以下是您可以克服的方法:
def progresses(ids: Seq[EntityId])Deferred来解析StudyNode,也可以通过EntityId使用groupBy。case class ProgressDeferred(entityId: EntityId) extends Deferred[Seq[Progress]]
class ProgressResolver extends DeferredResolver[YourContext] {
override def resolve(deferred: Vector[Deferred[Any]], ctx: YourContext, queryState: Any)(implicit ec: ExecutionContext): Vector[Future[Any]] = {
val entityIds = deferred.map{
case ProgressDeferred(entityId) => entityId
}
val progresses = ctx.progressService.progresses(entityIds).map(_.groupBy(_. entityId))
entityIds.map(id=> progresses.map(_.get(id)))
}
}https://stackoverflow.com/questions/49582268
复制相似问题