Search code examples
scalaslick

Transform DBIO Value to Custom Case Class


I have a DBIO[Seq[tuple]] and I would like to map it to DBIO[Seq[customCaseClass]].

I am aware that I could do the transformation on the db.run() result with something like: customCaseClass.tupled(row) (see this answer). However, I am interested in composing the DBIO return value in varying functions.


Solution

  • There are three places where you can do this: at the Query level, DBIO level, and (as you noted, and rejected) at the Future level.

    Query

    At the query level, the conversion will happen as part of the execution of the query on Slick's own execution context.

    It would look something like this:

    // Given some query that returns a tuple...
    val tupleQ: Query[(Rep[String],Rep[String]), (String,String), Seq] =
       table.map{ row => (row.column1, row.column2) }
    
    // ...which we'd like to project into this:
    case class SomeCaseClass(v1: String, v2: String)
    
    // ...we can use the mapTo macro to generate the conversion:
    val ccQ: Query[Rep[SomeCaseClass], SomeCaseClass, Seq] =
       tupleQ.map{ _.mapTo[SomeCaseClass] }
    

    If this was all you're doing, then maybe the default projection (def * ...) is the place for this.

    If you need more control over the conversion logic, you can use the lower-level <> in place of mapTo. Section 5.2 of Essential Slick gives more detail on this.

    DBIO

    The question was specifically about DBIO. The conversion there is going to run on your own execution context.

    That would look something like this:

    // Given a DBIO that returns a tuple...
    val tupleD: DBIO[Seq[(String,String)]] =
      table.map(row => (row.column1, row.column2)).result
    
    // ... we can use any of the DBIO combinators to convert it, such as map:
    val ccD: DBIO[Seq[SomeCaseClass]] =
      dQ.map{ pairs => pairs.map{ case (a, b) => SomeCaseClass(a,b) } }
    

    (...or dQ.map(pairs => pairs.map(SomeCaseClass.tupled)) as you noted).

    The two big benefit you get at this level are:

    1. you have access to the values, such as (a,b), and so can make decisions about what you want to do with the values.
    2. being part of an action means you could take part in a transcation.

    Chapter 4 of Essential Slick lists out many of the DBIO combinators. The Slick Manual also describes the combinators.

    Future

    The final place is in the Future, which looks very much like the DBIO version but after the db.run (as you've already found).