Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CQL.unbatched #16

Open
calvinlfer opened this issue Feb 13, 2022 · 2 comments
Open

Implement CQL.unbatched #16

calvinlfer opened this issue Feb 13, 2022 · 2 comments

Comments

@calvinlfer
Copy link
Member

Let's say you have a set of mutations in a single batch but you decide that you no longer want them in a batch:

val batchMutation = update1 + insert1 + update2 
val individualMutations: NonEmptyChunk[CQL[MutationResult]] = CQL.unbatched { batchMutation }

Currently, we track each individual mutation in a NonEmptyChunk but if we wanted to provide the user with each of the individual mutations in an API like so:

val batchMutation = update1 + insert1 + update2 
val u1 :*: i1 :*: u2 :*: Finish = CQL.unbatched { batchMutation }

Then we would need to use an HList and keep track of each insertion

@calvinlfer
Copy link
Member Author

Here's a possible implementation that provides an API which tracks each individual element of the Mutation:

package io.kaizensolutions.virgil.internal

import io.kaizensolutions.virgil.{CQL, MutationResult}
import zio.NonEmptyChunk

import scala.annotation.tailrec

sealed trait MutationSet {
  type Concat[In <: MutationSet] <: MutationSet
  def :*:[In <: CQL[MutationResult]](in: In): MutationSet
  def ++[That <: MutationSet](that: That): Concat[That]
}
object MutationSet {
  type :*:[Head <: CQL[MutationResult], Tail <: MutationSet] = Cons[Head, Tail]

  type Finish = Finish.type
  case object Finish extends MutationSet { self =>
    override type Concat[In <: MutationSet] = In

    override def :*:[In <: CQL[MutationResult]](in: In): In :*: Finish = Cons(in, self)
    override def ++[That <: MutationSet](that: That): Concat[That]     = that
  }

  final case class Cons[
    Head <: CQL[MutationResult],
    Tail <: MutationSet
  ](head: Head, tail: Tail)
      extends MutationSet { self =>
    override type Concat[In <: MutationSet] = Head :*: tail.Concat[In]

    override def :*:[In <: CQL[MutationResult]](in: In): In :*: Head :*: Tail = Cons(in, self)
    override def ++[That <: MutationSet](that: That): Concat[That]            = Cons(head, tail ++ that)

    def toNonEmptyChunk: NonEmptyChunk[CQL[MutationResult]] = {
      @tailrec
      def go(curr: MutationSet, acc: NonEmptyChunk[CQL[MutationResult]]): NonEmptyChunk[CQL[MutationResult]] =
        curr match {
          case Cons(head, tail) => go(tail, acc :+ head)
          case Finish           => acc
        }

      go(self.tail, NonEmptyChunk.single(head))
    }
  }

  def :*:[Head <: CQL[MutationResult], Tail <: MutationSet](head: Head, tail: Tail): Cons[Head, Tail] =
    Cons(head, tail)

  object :*: {
    def unapply[Head <: CQL[MutationResult], Tail <: MutationSet](in: Cons[Head, Tail]): Option[(Head, Tail)] =
      Some((in.head, in.tail))
  }
}

object Example extends App {
  import MutationSet._
  import io.kaizensolutions.virgil.cql._

  val mutations =
    cql"INSERT INTO users (id, name) VALUES (1, 'John')".mutation :*:
      cql"INSERT INTO users (id, name) VALUES (2, 'Jane')".mutation :*:
      cql"INSERT INTO users (id, name) VALUES (3, 'Jack)".mutation :*:
      Finish

  case class NewBatch[M <: MutationSet](mutations: M) {
    def unbatch: M = mutations
  }
  val batch                             = NewBatch(mutations)
  val john :*: jane :*: jack :*: Finish = batch.unbatch
}

Note that if you tried to add an extra element (for example: val john :*: jane :*: jack :*: jake :*: Finish = batch.unbatch) then it would fail at compile time.

image

You can also concatenate batches together and it will keep track of individual elements:

val john1 :*: jane1 :*: jack1 :*: john2 :*: jane2 :*: jack2 :*: Finish = batch.unbatch ++ batch.unbatch

This provides a richer API to the user so they can access their individual mutations or simply call toNonEmptyChunk if they don't want to individually address each element

@calvinlfer
Copy link
Member Author

Some additional work would also need to be done to CQL so we can distinguish a batch mutation from a single one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant