scalareflectionslick-3.0cake-pattern

Scala Slick single filter for multiple TableQuery instances


I'm using Scala Slick-3.1.0 lib.

How is it possible to make a generic Slick filter function that takes TableQuery instance as an input and makes same slick filter on it?

I have several case classes (two for example) representing data stored in DB. Some fields are the same so classes could possibly extend common ancestor:

case class FirstData(symbol: String,
                     date: Timestamp,
                     firstField: Double) 

case class SecondData(symbol: String,
                      date: Timestamp,
                      secondField: String) 

Each of them has its own SQL Table in DB and is represented by separate Slick Table class. Also they have same primary keys:

class FirstDataTable(tag: Tag) extends Table[FirstData](tag, "firstData") {
    def symbol = column[String]("symbol")
    def date = column[Timestamp]("date")
    def firstField= column[Double]("firstField")
    def * = (symbol, date, firstField) <> ((FirstData.apply _).tupled, FirstData.unapply)
    def pk = primaryKey("pk_firstData", (symbol, date))
}

class SecondDataTable(tag: Tag) extends Table[SecondData](tag, "secondData"){
    def symbol = column[String]("symbol")
    def date = column[Timestamp]("date")
    def secondField= column[String]("secondField")
    def * = (symbol, date, secondField) <> ((SecondData.apply _).tupled, SecondData.unapply)
    def pk = primaryKey("pk_secondData", (symbol, date))
}

Finally TableQuery classes are:

val firstDataTableQuery = TableQuery[FirstDataTable]
val secondDataTableQuery = TableQuery[SecondDataTable]
etc ...

How is it possible to make a generic Slick filter query function that takes firstDataTableQuery or secondDataTableQuery as an argument and makes same slick query on input. Filtering only on their common fields or another way saying on their SQL Table representations common columns. For example like this:

def filter(genericTableQuery: TableQuery) = {
    genericTableQuery.filter { data => dataFilterFunction(data.symbol)
    }.filter(_.date >= someDate).sortBy(data => data.date.asc)
}

val firstTableResult = filter(firstDataTableQuery)
val seconfTableResult = filter(secondDataTableQuery)
etc ...

I looked on this topics, but still could not make up a solution:

Slick 3 reusable generic repository

Scala reflection to instantiate scala.slick.lifted.TableQuery


Solution

  • invariant, thanks a lot. You pointed me to the right direction. I slightly modified your answer and everything works )

    Traits:

    trait Data {
                def symbol: String
                def date: Timestamp
                }
    
    trait GenericDataTable[T <: Data] extends Table[T] {
                def symbol: Rep[String]
                def date: Rep[Timestamp]
                def pk: PrimaryKey
                }
    

    FirstData and FirstDataTable classes look like this:

    case class FirstData(
                symbol: String,
                date: Timestamp,
                firstField: Double) extends Data
    
    class FirstDataTable(tag: Tag) extends Table[FirstData(tag,"firstData") 
        with GenericDataTable[FirstData] {
                def symbol = column[String]("symbol")
                def date = column[Timestamp]("date")
                def firstField= column[Double]("firstField")
                def * = (symbol, date, firstField) <> ((FirstData.apply _).tupled, FirstData.unapply)
                def pk = primaryKey("pk_firstData", (symbol, date))
        }
    

    Finally function looks like this:

    private def filter[M <: Data, T <: GenericDataTable[M]] (genericTableQuery: TableQuery[T]) = {
                genericTableQuery.filter { data => dataFilterFunction(data.symbol)}.
                filter(_.date >= someDate).sortBy(data => data.date.asc)
        }