2014-06-12 4 views
3

폭풍우 쿼리를 one of Play 2.3 samples에 매끄럽게 변환하려고하는데 동적 정렬을 구현하는 방법을 모르겠습니다.Slick (정렬)의 동적 쿼리 매개 변수

이 원래 방법 : 지금까지 내가 지금까지 첫 번째 쿼리와 함께이있어

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 

나는 매끄러운에 참여 "에 의해 순서"를해야합니까 방법 매개 변수로 메서드에 동적으로 전달되는 열 이름을 염두에 둡니까?

스칼라 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 (computer, company)'옵션을 사용하지 않으면,'SlickException : ResultSet 컬럼 경로에 대한 NULL 값 읽기 '가 발생합니다. 왼쪽 가입 회사의 경우 때때로 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 -> _.idid'는 확장 함수에 대한 누락 된 매개 변수 유형을 던졌습니다. ((x $ 4) => 1. $ minus $ greater (x $ 4). _1.id))'. 어떤 아이디어? – Caballero

+0

아마도'1 -> q => q._1.id'를 시도 할 것입니까? 그래도 작동하지 않는다면 맵을 사용하지 말고 패턴을 '일치'하여 동일한 결과를 얻으십시오. 불행히도 Slick의 유형은 매우 복잡하므로 제대로 작동하려면 약간의 피델을 사용해야합니다. – DCKing

+0

아니요. 작동하지 않으며,이 상황에서 일치하는 방법이나 위치를 잘 모릅니다. 나는 모든 가능한 조합을 시도했다.''- '(q => q.map (n => n._1.name))'은 여전히'Query [Column [String], String] 열 [_]'. 이것은 막 다른 골목처럼 보입니다 ... – Caballero

0

확실하지 않음이 분명 컴파일시 안전의 비용에있을 것입니다. 먼저 회사 사례 클래스를 Company.unapply를 사용하여 튜플로 변환 한 다음 엉성한의 at(N) 메서드를 사용합니다 (0부터 시작하는 인덱스 임). 다음 import

<dependency> 
    <groupId>com.chuusai</groupId> 
    <artifactId>shapeless_2.11</artifactId> 
    <version>2.3.1</version> 
</dependency> 
... 그리고 :

import shapeless.syntax.std.tuple._ 

사용이 기술에서 당신이 볼품해야합니다이를 위해

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

: 여기에 그 같을 것이다 무엇 너 자신의 위험.