Dynamic order by in scala slick with several columns

荒凉一梦 提交于 2019-12-05 11:26:53

Let's define DynamicSortBySupport helper

object DynamicSortBySupport {
  import slick.ast.Ordering.Direction
  import slick.ast.Ordering
  import slick.lifted.Query
  import slick.lifted.ColumnOrdered
  import slick.lifted.Ordered
  type ColumnOrdering = (String, Direction) //Just a type alias
  trait ColumnSelector {
    val select: Map[String, Rep[_]] //The runtime map between string names and table columns 
  }
  implicit class MultiSortableQuery[A <: ColumnSelector, B, C[_]](query: Query[A, B, C]) {
    def dynamicSortBy(sortBy: Seq[ColumnOrdering]): Query[A, B, C]  =
      sortBy.foldRight(query){ //Fold right is reversing order
        case ((sortColumn, sortOrder), queryToSort) =>
          val sortOrderRep: Rep[_] => Ordered = ColumnOrdered(_, Ordering(sortOrder))
          val sortColumnRep: A => Rep[_] = _.select(sortColumn)
          queryToSort.sortBy(sortColumnRep)(sortOrderRep)
      }
  }
}

and redefine your Table adding a "sorting map"

class ColorsTable(tag: Tag) extends Table[Color](tag, "color") with DynamicSortBySupport.ColumnSelector {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def * = (id, name) <> ((Color.apply _).tupled, Color.unapply)
  val select = Map(
    "id" -> (this.id),
    "name" -> (this.name)
  )
}

and finally use the whole thing in your code:

object FindAll extends App {
  import DynamicSortBySupport._
  import slick.ast.Ordering.Direction
  import slick.ast.Ordering
  object colors extends TableQuery(new ColorsTable(_))
  val sortsBy = Seq[(String, Direction)](("name", Ordering.Desc), ("id", Ordering.Asc)) //Replaced 
  val db = Database.forURL("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver="org.h2.Driver") //Just for testing
  findAll(sortsBy)
  def findAll(sortsBy: Seq[(String, Direction)]): Future[Seq[Color]] = {
    val query = colors.dynamicSortBy(sortsBy).result
    db.run(query)
  }
}

Notes and comments:

  • The runtime map Map[String, Rep[_]] between string names and table columns could be improved with error handling (now it just throws a runtime exception that must be managed properly) or automatic derivation from table definition itself;
  • I have replaced SortDirection with proper slick.ast.Ordering.Direction, feel free to write a converter;
  • You could also write an helper for optional filters, something like filterOption;
  • Note the use of foldRight to reverse sorting order;
  • If possible avoid vars and be functional :-)
  • If you want to have ColumnSelector outside the data layer (it is a good practice actually) you could rewrite it requiring implicitly something like ColumnSelector[T];
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!