2014-06-12 34 views
3

我試圖將異常查詢轉換爲浮動one of Play 2.3 samples,但我不確定如何實現動態排序。動態查詢參數(排序)

這是原始的方法:

def list(page: Int = 0, pageSize: Int = 10, orderBy: Int = 1, filter: String = "%"): Page[(Computer, Option[Company])] = { 

    val offest = pageSize * page 

    DB.withConnection { implicit connection => 

     val computers = SQL(
     """ 
      select * from computer 
      left join company on computer.company_id = company.id 
      where computer.name like {filter} 
      order by {orderBy} nulls last 
      limit {pageSize} offset {offset} 
     """ 
     ).on(
      'pageSize -> pageSize, 
      'offset -> offest, 
      'filter -> filter, 
      'orderBy -> orderBy 
     ).as(Computer.withCompany *) 

     val totalRows = SQL(
     """ 
      select count(*) from computer 
      left join company on computer.company_id = company.id 
      where computer.name like {filter} 
     """ 
     ).on(
      'filter -> filter 
     ).as(scalar[Long].single) 

     Page(computers, page, offest, totalRows) 

    } 

} 

到目前爲止,我走了這麼遠與第一查詢:

val computers_ = (for { 
    (computer, company) <- Computer.where(_.name like filter) leftJoin 
     Company on (_.companyId === _.id) 
} yield (computer, company.?)).list 

如何「按訂單」部分光滑做,記住它是一個作爲參數動態傳遞給方法的列名?

Scala的2.10.4 /播放下面的油滑碼發生器產生2.3 /油滑2.0.2

表類:

package tables 
// AUTO-GENERATED Slick data model 
/** Stand-alone Slick data model for immediate use */ 
object Tables extends { 
    val profile = scala.slick.driver.H2Driver 
} with Tables 

/** Slick data model trait for extension, choice of backend or usage in the cake pattern. (Make sure to initialize this late.) */ 
trait Tables { 
    val profile: scala.slick.driver.JdbcProfile 
    import profile.simple._ 
    import scala.slick.model.ForeignKeyAction 
    // NOTE: GetResult mappers for plain SQL are only generated for tables where Slick knows how to map the types of all columns. 
    import scala.slick.jdbc.{GetResult => GR} 

    /** DDL for all tables. Call .create to execute. */ 
    lazy val ddl = Company.ddl ++ Computer.ddl 

    /** Entity class storing rows of table Company 
    * @param id Database column ID PrimaryKey 
    * @param name Database column NAME */ 
    case class CompanyRow(id: Long, name: String) 
    /** GetResult implicit for fetching CompanyRow objects using plain SQL queries */ 
    implicit def GetResultCompanyRow(implicit e0: GR[Long], e1: GR[String]): GR[CompanyRow] = GR{ 
    prs => import prs._ 
    CompanyRow.tupled((<<[Long], <<[String])) 
    } 
    /** Table description of table COMPANY. Objects of this class serve as prototypes for rows in queries. */ 
    class Company(tag: Tag) extends Table[CompanyRow](tag, "COMPANY") { 
    def * = (id, name) <> (CompanyRow.tupled, CompanyRow.unapply) 
    /** Maps whole row to an option. Useful for outer joins. */ 
    def ? = (id.?, name.?).shaped.<>({r=>import r._; _1.map(_=> CompanyRow.tupled((_1.get, _2.get)))}, (_:Any) => throw new Exception("Inserting into ? projection not supported.")) 

    /** Database column ID PrimaryKey */ 
    val id: Column[Long] = column[Long]("ID", O.PrimaryKey) 
    /** Database column NAME */ 
    val name: Column[String] = column[String]("NAME") 
    } 
    /** Collection-like TableQuery object for table Company */ 
    lazy val Company = new TableQuery(tag => new Company(tag)) 

    /** Entity class storing rows of table Computer 
    * @param id Database column ID PrimaryKey 
    * @param name Database column NAME 
    * @param introduced Database column INTRODUCED 
    * @param discontinued Database column DISCONTINUED 
    * @param companyId Database column COMPANY_ID */ 
    case class ComputerRow(id: Long, name: String, introduced: Option[java.sql.Timestamp], discontinued: Option[java.sql.Timestamp], companyId: Option[Long]) 
    /** GetResult implicit for fetching ComputerRow objects using plain SQL queries */ 
    implicit def GetResultComputerRow(implicit e0: GR[Long], e1: GR[String], e2: GR[Option[java.sql.Timestamp]], e3: GR[Option[Long]]): GR[ComputerRow] = GR{ 
    prs => import prs._ 
    ComputerRow.tupled((<<[Long], <<[String], <<?[java.sql.Timestamp], <<?[java.sql.Timestamp], <<?[Long])) 
    } 
    /** Table description of table COMPUTER. Objects of this class serve as prototypes for rows in queries. */ 
    class Computer(tag: Tag) extends Table[ComputerRow](tag, "COMPUTER") { 
    def * = (id, name, introduced, discontinued, companyId) <> (ComputerRow.tupled, ComputerRow.unapply) 
    /** Maps whole row to an option. Useful for outer joins. */ 
    def ? = (id.?, name.?, introduced, discontinued, companyId).shaped.<>({r=>import r._; _1.map(_=> ComputerRow.tupled((_1.get, _2.get, _3, _4, _5)))}, (_:Any) => throw new Exception("Inserting into ? projection not supported.")) 

    /** Database column ID PrimaryKey */ 
    val id: Column[Long] = column[Long]("ID", O.PrimaryKey) 
    /** Database column NAME */ 
    val name: Column[String] = column[String]("NAME") 
    /** Database column INTRODUCED */ 
    val introduced: Column[Option[java.sql.Timestamp]] = column[Option[java.sql.Timestamp]]("INTRODUCED") 
    /** Database column DISCONTINUED */ 
    val discontinued: Column[Option[java.sql.Timestamp]] = column[Option[java.sql.Timestamp]]("DISCONTINUED") 
    /** Database column COMPANY_ID */ 
    val companyId: Column[Option[Long]] = column[Option[Long]]("COMPANY_ID") 

    /** Foreign key referencing Company (database name FK_COMPUTER_COMPANY_1) */ 
    lazy val companyFk = foreignKey("FK_COMPUTER_COMPANY_1", companyId, Company)(r => r.id, onUpdate=ForeignKeyAction.Restrict, onDelete=ForeignKeyAction.Restrict) 
    } 
    /** Collection-like TableQuery object for table Computer */ 
    lazy val Computer = new TableQuery(tag => new Computer(tag)) 
} 

UPDATE - 解最終溶液是在this question

回答

2

我的第一個答案在正確的位置插入了排序功能,但由於Slick的複雜輸入而迅速變得複雜。您可以通過使用Slick的查詢組合基於所需的順序直接修改查詢來避免這些輸入問題。

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 
} 
+0

感謝您的努力,現在真的很接近,但是...如果我離開'yield(電腦,公司)' - 公司不是可選的,我得到'SlickException:讀取ResultSet列路徑的NULL值 - 這是因爲左連接公司有時會導致空值。如果我離開公司可選'yield(computer,company。?)','case 3 => c.sortBy(_._ 2.name)',這是指公司裏的一個專欄給我'無法解析符號名稱' ,我猜,因爲公司在這一點上是可選的... – Caballero

+0

@Caballero是的,它變得荒謬!我實際上並不知道如何克服最後的問題......也許這是一個不同的SO問題的好材料? – DCKing

+0

感謝您的幫助。是的,這是非常煩人的。我希望Slick團隊的某個人最終會參與進來。 – Caballero

1

Slick和Anorm之間的區別在於Slick的查詢由Scala編譯器檢查。實現這樣一個動態參數需要在Slick中做更多的工作,但是你會得到類型安全的回報。由於您的查詢排序是多個表的連接,因此在這種情況下執行起來尤其麻煩。

在一般情況下,它應該大致是這樣的:

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 
    //.. 

} 

的總體思路,以您收到您要排序的列油滑的整數映射是回答你的問題。

+0

謝謝,這看起來像是正確的方向,但是'1 - > _._ 1.id'爲擴展函數((x $ 4)=> 1)拋出一個錯誤'缺少參數類型。$ minus $ greater(x $ 4。 _1.id))'。有任何想法嗎? – Caballero

+0

也許試試'1 - > q => q._1.id'?如果這不起作用,請嘗試不使用地圖,而是使用匹配模式來完成相同的操作。不幸的是,Slick的類型非常複雜,所以你必須小心一些才能使它工作。 – DCKing

+0

不,這不起作用,我不知道在這種情況下如何或在哪裏使用匹配。我嘗試了所有可能的組合,直到'1 - >(q => q.map(n => n._1.name))'仍然返回Query [Column [String],String]'而不是'柱[_]'。這看起來像一個死衚衕...... – Caballero

0

不知道這是在世界上最好的想法,但你可以在技術上使用不成形,以幫助你得到一個編號的元組元素,這顯然會在編譯時安全性的成本。首先使用Company.unapply將公司案例類轉換爲元組,然後使用無形的at(N)方法(請注意,它是基於0的索引)。下面是什麼樣子:

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 
    //.. 
} 

爲了做到這一點,你需要不成形:

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

...和下​​面的導入:

import shapeless.syntax.std.tuple._ 

使用這種技術在你自己的風險。