Skip to content

Instantly share code, notes, and snippets.

@calvinlfer
Created November 25, 2022 17:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save calvinlfer/5355c945374803b10e7345daf280343f to your computer and use it in GitHub Desktop.
Save calvinlfer/5355c945374803b10e7345daf280343f to your computer and use it in GitHub Desktop.
In memory one to many join
def joinLManyR[Err, LeftElem, LeftKey, MiddleElem, MiddleKey, RightElem, RightKey, RightProjection](left: Iterable[LeftKey])(
middle: Iterable[LeftKey] => Stream[Err, MiddleElem]
)(middleKey: MiddleElem => MiddleKey)(middleToLeftKey: MiddleElem => LeftKey)(middleToRightKey: MiddleElem => RightKey)(
right: Iterable[RightKey] => Stream[Err, RightElem]
)(rightKey: RightElem => RightKey)(rightProjection: RightElem => RightProjection): IO[Err, Map[LeftKey, Set[RightProjection]]] =
val extractMiddle: IO[Err, Chunk[MiddleElem]] = middle(left).runCollect
def rightElems(in: Iterable[RightKey]): IO[Err, Chunk[RightElem]] =
right(in).runCollect
def rightLookup(in: Iterable[RightElem]): Map[RightKey, RightElem] =
in.foldLeft(Map.empty[RightKey, RightElem]) { (acc, next) =>
val key = rightKey(next)
acc + (key -> next)
}
def leftToManyRight(middleElems: Iterable[MiddleElem], rightLookup: Map[RightKey, RightElem]): Map[LeftKey, Set[RightProjection]] =
val rightTargetProject = middleToRightKey andThen rightLookup andThen rightProjection
middleElems.foldLeft(Map.empty[LeftKey, Set[RightProjection]]) { (acc, next) =>
val leftKey = middleToLeftKey(next)
val rightP = rightTargetProject(next)
val existing = acc.getOrElse(leftKey, Set.empty)
val updated = existing + rightP
acc + (leftKey -> updated)
}
for
middle <- extractMiddle
rElems <- rightElems(middle.map(middleToRightKey))
rLookup = rightLookup(rElems)
result = leftToManyRight(middle, rLookup)
yield result
@calvinlfer
Copy link
Author

calvinlfer commented Nov 25, 2022

  def joinLManyR[Err, LeftElem, LeftKey, MiddleElem, MiddleKey, RightElem, RightKey, RightProjection](left: Iterable[LeftKey])(
    middle: Iterable[LeftKey] => Stream[Err, MiddleElem]
  )(middleKey: MiddleElem => MiddleKey)(middleToLeftKey: MiddleElem => LeftKey)(middleToRightKey: MiddleElem => RightKey)(
    right: Iterable[RightKey] => Stream[Err, RightElem]
  )(rightKey: RightElem => RightKey)(rightProjection: RightElem => RightProjection): IO[Err, Map[LeftKey, Set[RightProjection]]] =
    val extractMiddle: IO[Err, Chunk[MiddleElem]] = middle(left).runCollect

    def rightFetchAndLookup(in: Iterable[RightKey]): IO[Err, Map[RightKey, RightElem]] =
      right(in).runFold(Map.empty[RightKey, RightElem]) { (acc, next) =>
        val key = rightKey(next)
        acc + (key -> next)
      }

    def leftToManyRight(middleElems: Iterable[MiddleElem], rightLookup: Map[RightKey, RightElem]): Map[LeftKey, Set[RightProjection]] =
      val rightTargetProject = middleToRightKey andThen rightLookup andThen rightProjection

      middleElems.foldLeft(Map.empty[LeftKey, Set[RightProjection]]) { (acc, next) =>
        val leftKey  = middleToLeftKey(next)
        val rightP   = rightTargetProject(next)
        val existing = acc.getOrElse(leftKey, Set.empty)
        val updated  = existing + rightP

        acc + (leftKey -> updated)
      }

    for
      middle  <- extractMiddle
      rLookup <- rightFetchAndLookup(middle.map(middleToRightKey))
      result   = leftToManyRight(middle, rLookup)
    yield result

Usage:

def catLookup(ids: Iterable[GroupInternalId]): IO[PcmError, Map[GroupInternalId, Set[CategoryCode]]] =
    joinLManyR[
      PcmError,
      DbGroup,
      GroupInternalId,
      DbGroupCategoryRelation,
      GroupCategoryInternalId,
      DbCategory,
      CategoryInternalId,
      CategoryCode
    ](
      ids
    )(groupInternalIds =>
      ZPGIOInterpreter
        .interpret(repo.findManyGroupCategoryRelations(groupInternalIds))
        .mapError(databaseError)
        .provideEnvironment(env)
    )(_.id)(_.groupInternalId)(_.categoryInternalId)(categoryInternalIds =>
      ZPGIOInterpreter
        .interpret(repo.findCategories(categoryInternalIds))
        .mapError(databaseError)
        .provideEnvironment(env)
    )(_.id)(_.code)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment