Каков наилучший способ передачи аргументов поля (например, параметров подкачки) в отложенный `Fetcher`?
Ниже приведен пример того, как я в настоящее время обрабатываю аргументы отложенного поля.
Parent
класс содержит отложенный Page
из Child
объекты. Параметры подкачки для детей определены на children
поле. Мне нужно иметь возможность передать параметры подкачки, вместе с идентификатором родителя, отложенному Fetcher
Я собираю их во временный DeferredChildInput
объект, вместе с идентификатором родителя, и передать их Fetcher
, Соответствующий DeferredChildResult
возвращает результат запроса (Page[Child]
) и DeferredChildInput
(используйте в HasId
).
Вопрос в том, есть ли лучший способ передать аргументы поля и родительский идентификатор отложенному 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
}
}
)
)
)
2 ответа
В этом сценарии вы не пользуетесь средствами извлечения, а просто увеличивает сложность механизма извлечения данных. Например, весьма маловероятно, что результаты будут кэшированы и использованы повторно. Вы также решаете каждый DeferredChildInput
отдельно, который побеждает основное назначение сборщиков (то есть пакетную выборку данных, где данные для всех inputs
выбирается в одном запросе / взаимодействии с БД).
В этом сценарии я бы рекомендовал вообще не использовать средства извлечения и получать данные страницы непосредственно из функции разрешения. Сборщики не поддерживают пагинацию. В некоторых сценариях может быть целесообразно извлечь идентификаторы сущностей в функции разрешения и затем использовать сборщик для извлечения данных сущностей на основе уже известного списка идентификаторов. Но, насколько я могу судить, это не так в вашем сценарии.
Также при использовании распознавателей и возврате N родительских объектов вы получите N+1 выборок для каждого дочернего объекта, что намного медленнее, чем выборка n страниц дочерних объектов и группировка по идентификатору родительского объекта.