首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Sangria进行查询设计以防止开销

使用Sangria进行查询设计以防止开销
EN

Stack Overflow用户
提问于 2018-03-30 22:33:23
回答 2查看 390关注 0票数 1

我有一个关于模式/查询设计的问题。

假设有一个Study模型。

Study可以有多个接班人,所以有明显的parentId字段。

代码语言:javascript
复制
case class Study(id: StudyId, name: String, parentId: Option[StudyId])

上下文中的User可以访问学习,因此我们希望将所有可访问的研究作为树返回到UI。因此,查询字段可以如下所示

代码语言:javascript
复制
Field(name = "accessibleStudies", 
      fieldType = ListType(StudyNode.Type), 
      resolve = implicit ctx => inject[StudyQueries].accessibleStudies(ctx))

accessibleStudies方法中,我们要求DB提供数据,并准备可以作为树在UI上查看的列表。

代码语言:javascript
复制
class StudyQueries {
    def accessibleStudies(ctx: Ctx): Future[Seq[StudyNode]] = {
        // 50 lines of data fetching and transformation
    }
}

现在有趣的部分。StudyNode应该有额外的字段- progress,它可以被计算为所有继承者的进展的递归和,因此我们需要每次都要有完整的可访问的研究列表来获得单个节点的progress。所以简单的方法就像

代码语言:javascript
复制
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定义,现在可以这样修改。

代码语言:javascript
复制
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字段。现在我想知道是否有更好的方法来处理这种情况。

EN

回答 2

Stack Overflow用户

发布于 2018-07-03 09:47:58

您可以使用Deferred Resolvers / Fetchers,它可以在查询执行过程中缓存相同ids的结果,因此不需要询问数据库两次。

更多关于这一点的信息:

https://sangria-graphql.org/learn/#high-level-fetch-api

resolvers/

此外,您还可以使用任何第三方库来进行更有效的获取,例如

http://47deg.github.io/fetch/

https://github.com/getclump/clump

票数 0
EN

Stack Overflow用户

发布于 2018-07-11 03:46:20

我相信您正在尝试避免N+1查询。以下是您可以克服的方法:

  1. 按多个ID获取进度列表。def progresses(ids: Seq[EntityId])
  2. 现在,您可以使用Sangria提供的Deferred来解析StudyNode,也可以通过EntityId使用groupBy

case class ProgressDeferred(entityId: EntityId) extends Deferred[Seq[Progress]]

代码语言:javascript
复制
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)))
  }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49582268

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档