aboutsummaryrefslogblamecommitdiff
path: root/src/main/scala/xyz/driver/restquery/db/SlickQueryBuilder.scala
blob: 2947b3cc2f03ac69a7f5905c9a4c30b978b8eb54 (plain) (tree)
1
2
3
4
5
6
7
                               
 
                                             

                              
                   
                                   















                                                                



                                                 




                                                              






                                                

     





                                                                  



                                                                                                              






                                                      





                                                                       
                                                           


                       

                  




                                                                    






                                                      






                                                                         
                                                       



                       

                  


                                                                                 

                                                 







































                                                                                                        
package xyz.driver.restquery.db

import java.sql.{JDBCType, PreparedStatement}
import java.time.LocalDateTime

import slick.jdbc._
import xyz.driver.restquery.query._

import scala.concurrent.{ExecutionContext, Future}

object SlickQueryBuilder {

  type Runner[T] = SlickQueryBuilderParameters => Future[Seq[T]]

  type CountResult = Future[(Int, Option[LocalDateTime])]

  type CountRunner = SlickQueryBuilderParameters => CountResult

  /**
    * Binder for PreparedStatement
    */
  type Binder = PreparedStatement => PreparedStatement

  final case class TableData(
      tableName: String,
      lastUpdateFieldName: Option[String] = None,
      nullableFields: Set[String] = Set.empty)

  val AllFields = Set("*")

  implicit class SQLActionBuilderConcat(a: SQLActionBuilder) {
    def concat(b: SQLActionBuilder): SQLActionBuilder = {
      SQLActionBuilder(
        a.queryParts ++ b.queryParts,
        (p: Unit, pp: PositionedParameters) => {
          a.unitPConv.apply(p, pp)
          b.unitPConv.apply(p, pp)
        }
      )
    }
  }

  implicit object SetQueryParameter extends SetParameter[AnyRef] {
    def apply(v: AnyRef, pp: PositionedParameters) = {
      pp.setObject(v, JDBCType.BINARY.getVendorTypeNumber)
    }
  }
}

final case class SlickTableLink(keyColumnName: String, foreignTableName: String, foreignKeyColumnName: String)

final case class SlickPostgresQueryBuilderParameters(
    databaseName: String,
    tableData: SlickQueryBuilder.TableData,
    links: Map[String, SlickTableLink] = Map.empty,
    filter: SearchFilterExpr = SearchFilterExpr.Empty,
    sorting: Sorting = Sorting.Empty,
    pagination: Option[Pagination] = None)
    extends SlickQueryBuilderParameters {

  def limitToSql()(implicit profile: JdbcProfile): SQLActionBuilder = {
    import profile.api._
    pagination.map { pagination =>
      val startFrom = (pagination.pageNumber - 1) * pagination.pageSize
      sql"limit #${pagination.pageSize} OFFSET #$startFrom"
    } getOrElse (sql"")
  }

  val qs = """""""

}

/**
  * @param links Links to another tables grouped by foreignTableName
  */
final case class SlickMysqlQueryBuilderParameters(
    databaseName: String,
    tableData: SlickQueryBuilder.TableData,
    links: Map[String, SlickTableLink] = Map.empty,
    filter: SearchFilterExpr = SearchFilterExpr.Empty,
    sorting: Sorting = Sorting.Empty,
    pagination: Option[Pagination] = None)
    extends SlickQueryBuilderParameters {

  def limitToSql()(implicit profile: JdbcProfile): SQLActionBuilder = {
    import profile.api._
    pagination
      .map { pagination =>
        val startFrom = (pagination.pageNumber - 1) * pagination.pageSize
        sql"limit #$startFrom, #${pagination.pageSize}"
      }
      .getOrElse(sql"")
  }

  val qs = """`"""

}

abstract class SlickQueryBuilder[T](val parameters: SlickQueryBuilderParameters)(
    implicit runner: SlickQueryBuilder.Runner[T],
    countRunner: SlickQueryBuilder.CountRunner) {

  def run()(implicit ec: ExecutionContext): Future[Seq[T]] = runner(parameters)

  def runCount()(implicit ec: ExecutionContext): SlickQueryBuilder.CountResult = countRunner(parameters)

  /**
    * Runs the query and returns total found rows without considering of pagination.
    */
  def runWithCount()(implicit ec: ExecutionContext): Future[(Seq[T], Int, Option[LocalDateTime])] = {
    for {
      all                 <- run
      (total, lastUpdate) <- runCount
    } yield (all, total, lastUpdate)
  }

  def withFilter(newFilter: SearchFilterExpr): SlickQueryBuilder[T]

  def withFilter(filter: Option[SearchFilterExpr]): SlickQueryBuilder[T] = {
    filter.fold(this)(withFilter)
  }

  def resetFilter: SlickQueryBuilder[T] = withFilter(SearchFilterExpr.Empty)

  def withSorting(newSorting: Sorting): SlickQueryBuilder[T]

  def withSorting(sorting: Option[Sorting]): SlickQueryBuilder[T] = {
    sorting.fold(this)(withSorting)
  }

  def resetSorting: SlickQueryBuilder[T] = withSorting(Sorting.Empty)

  def withPagination(newPagination: Pagination): SlickQueryBuilder[T]

  def withPagination(pagination: Option[Pagination]): SlickQueryBuilder[T] = {
    pagination.fold(this)(withPagination)
  }

  def resetPagination: SlickQueryBuilder[T]

}