Edmondo
Edmondo

Reputation: 20090

CaseClassShape for model with computed properties

When using Slick CaseClass shape, I am currently facing a situation where for efficiency reasons of a groupBy clause, I would need three columns to be stored in the relational model, while only two fields exists in my object oriented model ( the third being computed)

case class MyEntry(quantity:Double, time:LocalDateTime){

   val weight = time.getHour match {
     case x if x>=7 && x<=23 => 0.1
     case _ => 0.05
   }

 }

 case class LiftedMyTuple(quantity:Rep[Double],time:Rep[LocalDateTime], weight:Rep[Double])

implicit object MyEntryShape extends CaseClassShape(???,???)

 class MyTableRow(tag:Tag) extends Table[MyEntry](tag, "MY_TABLE"){

   def quantity = column[Double]("QUANTITY")

   def time = column[LocalDateTime]("TIME",O.PrimaryKey)

   def weight = column[Double]("WEIGHT")

   def * = LiftedMyTuple(quantity,time,weight)

 }

How does one write a CaseClassShape for such a usage, where the case class has a computed property which should appear in the relational model?

Upvotes: 0

Views: 542

Answers (1)

Daenyth
Daenyth

Reputation: 37461

It looks like the constructor is CaseClassShape(mapLifted: (LiftedTuple) ⇒ LiftedCaseClass, mapPlain: (PlainTuple) ⇒ PlainCaseClass)

We should be able to satisfy that like this:

object myShape extends
  CaseClassShape(
    LiftedMyTuple.tupled,
    { (quantity, time, ignored) => MyEntry(quantity, time) }
  )

If you change your case class to store weight but provide a constructor that computes it, you can get read-from-db behavior on the db->code side and compute behavior on the code->db side.

object MyEntry {
  def apply(quantity: Double, time: LocalDateTime) = {
    val weight = time.getHour match {
      case x if x>=7 && x<=23 => 0.1
      case _ => 0.05
    }
    MyEntry(quantity, time, weight)
  }
}
case class MyEntry(quantity: Double, time: LocalDateTime, weight: Double)

object myShape extends CaseClassShape(LiftedMyTuple.tupled, MyShape.apply.tupled)

val example = MyShape(0.0, now) // computes weight

Note that I'm calling MyShape.apply explicitly instead of MyShape - because we've created a new apply method the compiler will no longer automatically infer that MyShape means MyShape.apply (when it's being passed as a function).

Upvotes: 1

Related Questions