Dynamic query parameters in Slick (sorting)

泄露秘密 提交于 2019-12-05 22:40:22

My first answer plugs in the sorting function at the right place, but quickly grows complicated because of Slick's complicated typing. You can avoid these typing issues by using Slick's query composition to modify the query directly based on the desired ordering.

def list(page: Int = 0, pageSize: Int = 10, orderBy: Int = 1, filter: String = "%") = {
   //..
   val q = for {
     (computer, company) <- Computer.where(_.name like filter) leftJoin
                            Company on (_.companyId === _.id)
   } yield (computer, company.?)

   val sortedQ = orderBy match {
     case 1 => q.sortBy(_._1.id)
     case 2 => q.sortBy(_._1.description)
     // Others
   }

   val pagedQ = sortedQ.drop(page * pageSize).take(pageSize)

   pagedQ.list
}

The difference between Slick and Anorm is that Slick's queries are checked by the Scala compiler. Implementing such a dynamic parameter takes a bit more effort in Slick, but you get type safety in return. It is made particularly cumbersome to do in this case since your query ordering is a join of multiple tables.

In general, it should look roughly like this:

def orderings(code: Int): ((Computer, Company)) => Column[_] = {
   code match {
      case 1 => _._1.id
      case 2 => _._1.description
      // Other orderings
   }
)

def list(page: Int = 0, pageSize: Int = 10, orderBy: Int = 1, filter: String = "%") = {
   //..
   val computers_ = (for {
     (computer, company) <- Computer.where(_.name like filter) leftJoin
                            Company on (_.companyId === _.id)
   } yield (computer, company.?))
   .sortBy(orderings(orderBy).nullsLast)
   .drop(page * pageSize)
   .take(pageSize)
   .list
   //..

}

The general idea to map the integers you receive to the Slick columns on which you want to sort is the answer to your question.

Not sure if this is the best idea in the world but you could technically use shapeless to help you get a numbered tuple element, this will obviously be at the cost of compile-time safety. First convert the Company case class into a tuple with Company.unapply and then use shapeless's at(N) method (note that it's a 0-based index). Here's what that would look like:

def list(page: Int = 0, pageSize: Int = 10, orderBy: Int = 1, filter: String = "%") = {
   //..
   val computers_ = (for {
     (computer, company) <- Computer.where(_.name like filter) leftJoin
                            Company on (_.companyId === _.id)
   } yield (computer, company.?))
   .sortBy(Company.unapply(_._1).get.at(orderBy-1).nullsLast)
   .drop(page * pageSize)
   .take(pageSize)
   .list
   //..
}

In order to do this you will need shapeless:

<dependency>
    <groupId>com.chuusai</groupId>
    <artifactId>shapeless_2.11</artifactId>
    <version>2.3.1</version>
</dependency>

...and the following import:

import shapeless.syntax.std.tuple._

Use this technique at your own risk.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!