Каков наилучший способ передачи аргументов поля (например, параметров подкачки) в отложенный `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 страниц дочерних объектов и группировка по идентификатору родительского объекта.

Другие вопросы по тегам