将字段参数(例如分页参数)传递给延迟的“Fetcher”的最佳方式是什么?
What's the best way to pass field arguments (e.g. paging parameters) to a deferred `Fetcher`?
下面是我当前如何处理延迟字段参数的示例。
A Parent
class 包含延迟 Page
Child
object 秒。 children 的分页参数在 children
字段中定义。我需要能够将分页参数连同 parent 的 ID 一起传递给延迟的 Fetcher
,所以我将它们捆绑在一个临时的 DeferredChildInput
object 中,连同 parent 的 ID,并将它们传递给 Fetcher
。对应DeferredChildResult
returns查询结果(Page[Child]
)和DeferredChildInput
(在HasId
中使用)。
问题是,是否有更好的方法将字段参数和 parent id 传递给延迟的 Fetcher
?
case class Page[T](
data: Seq[T],
pageNumber: Int,
pageSize: Int,
totalRecords: Long
)
case class Parent(
id: Long,
children: Page[Children] // this field is deferred
)
case class Child(
id: Long
parentId: Long
)
// the field's query parameters
case class DeferredChildInput(
parentId: Long,
pageNumber: Int,
pageSize: Int
)
// used to temporarily hold the result of the deferred resolution
case class DeferredChildResult(
input: DeferredChildInput // this is used to resolve the HasId check
page: Page[Child] // this is what we really want
)
trait ChildService {
def getChildrenByParentId(
parentId: Long,
pageNumber: Int,
pageSize: Int
): Page[Child]
}
val childFetcher: Fetcher[MyContext, DeferredChildResult, DeferredChildResult, DeferredChildInput] = Fetcher {
(ctx: MyContext, inputs: Seq[DeferredChildInput]) =>
val futures = inputs.map { input =>
ctx.childService.getChildrenByParentId(
input.parentId,
input.pageNumber,
input.pageSize
).map { childPage =>
DeferredChildResult(input, childPage)
}
}
Future.sequence {
futures
}
}(HasId(_.input))
}
val ChildObjectType = derivedObjectType[Unit, Child]()
val ParentObjectType = deriveObjectType[Unit, Parent](
ReplaceField(
fieldName = "children",
field = Field(
name = "children",
fieldType = PageType(childObjectType),
arguments = List(
Argument(
name = "pageNumber",
argumentType = IntType
), Argument(
name = "pageSize",
argumentType = IntType,
)
),
resolve = ctx => {
// bundle the field/query parameters into a single input object
val input = DeferredChildInput(
parentId = ctx.value.id,
pageNumber = ctx.args[Int]("pageNumber"),
pageSize = ctx.args[Int]("pageSize")
)
DeferredValue(childFetcher.defer(input)).map { results =>
results.page
}
}
)
)
)
在这种情况下,您并没有真正从使用提取器中获益,它只是增加了数据提取机制的复杂性。例如,结果不太可能被缓存和重用。您还分别解析了每个 DeferredChildInput
,这违背了获取程序的主要目的(即批量获取数据,其中所有 inputs
的数据都是在单个 request/DB 交互中获取的)。
我建议在这种情况下完全避免使用提取器,而是直接从解析函数中提取页面数据。 Fetcher 并不真正支持分页。在某些情况下,在 resolve 函数中获取实体的 ID,然后使用 fetcher 根据已知的 ID 列表获取实体数据可能是可行的。但据我所知,你的情况并非如此。
此外,当使用解析器并返回 N 个父对象时,您最终会为每个子对象获取 N+1 次,这比获取 n 页子对象并按父对象 ID 分组要慢得多。
下面是我当前如何处理延迟字段参数的示例。
A Parent
class 包含延迟 Page
Child
object 秒。 children 的分页参数在 children
字段中定义。我需要能够将分页参数连同 parent 的 ID 一起传递给延迟的 Fetcher
,所以我将它们捆绑在一个临时的 DeferredChildInput
object 中,连同 parent 的 ID,并将它们传递给 Fetcher
。对应DeferredChildResult
returns查询结果(Page[Child]
)和DeferredChildInput
(在HasId
中使用)。
问题是,是否有更好的方法将字段参数和 parent id 传递给延迟的 Fetcher
?
case class Page[T](
data: Seq[T],
pageNumber: Int,
pageSize: Int,
totalRecords: Long
)
case class Parent(
id: Long,
children: Page[Children] // this field is deferred
)
case class Child(
id: Long
parentId: Long
)
// the field's query parameters
case class DeferredChildInput(
parentId: Long,
pageNumber: Int,
pageSize: Int
)
// used to temporarily hold the result of the deferred resolution
case class DeferredChildResult(
input: DeferredChildInput // this is used to resolve the HasId check
page: Page[Child] // this is what we really want
)
trait ChildService {
def getChildrenByParentId(
parentId: Long,
pageNumber: Int,
pageSize: Int
): Page[Child]
}
val childFetcher: Fetcher[MyContext, DeferredChildResult, DeferredChildResult, DeferredChildInput] = Fetcher {
(ctx: MyContext, inputs: Seq[DeferredChildInput]) =>
val futures = inputs.map { input =>
ctx.childService.getChildrenByParentId(
input.parentId,
input.pageNumber,
input.pageSize
).map { childPage =>
DeferredChildResult(input, childPage)
}
}
Future.sequence {
futures
}
}(HasId(_.input))
}
val ChildObjectType = derivedObjectType[Unit, Child]()
val ParentObjectType = deriveObjectType[Unit, Parent](
ReplaceField(
fieldName = "children",
field = Field(
name = "children",
fieldType = PageType(childObjectType),
arguments = List(
Argument(
name = "pageNumber",
argumentType = IntType
), Argument(
name = "pageSize",
argumentType = IntType,
)
),
resolve = ctx => {
// bundle the field/query parameters into a single input object
val input = DeferredChildInput(
parentId = ctx.value.id,
pageNumber = ctx.args[Int]("pageNumber"),
pageSize = ctx.args[Int]("pageSize")
)
DeferredValue(childFetcher.defer(input)).map { results =>
results.page
}
}
)
)
)
在这种情况下,您并没有真正从使用提取器中获益,它只是增加了数据提取机制的复杂性。例如,结果不太可能被缓存和重用。您还分别解析了每个 DeferredChildInput
,这违背了获取程序的主要目的(即批量获取数据,其中所有 inputs
的数据都是在单个 request/DB 交互中获取的)。
我建议在这种情况下完全避免使用提取器,而是直接从解析函数中提取页面数据。 Fetcher 并不真正支持分页。在某些情况下,在 resolve 函数中获取实体的 ID,然后使用 fetcher 根据已知的 ID 列表获取实体数据可能是可行的。但据我所知,你的情况并非如此。
此外,当使用解析器并返回 N 个父对象时,您最终会为每个子对象获取 N+1 次,这比获取 n 页子对象并按父对象 ID 分组要慢得多。