2016-05-17 7 views
2

Ich versuche dynamisch eine SortBy zu meiner Abfrage, die basierend auf seinem String-Namen aus einem Abfrageparameter sortiert. In Slick 3 hat sich das als recht knifflig erwiesen. Derzeit ist mein Setup ist:Dynamische SortBy mit Slick 3

trait Model { 
    type ATable <: AbstractTable[_] 

    def tableQuery: TableQuery[ATable] 

    def sortMap: Map[String, Rep[_]] 

    private def sortKey[T](e: ATable, sort: (String, SortOrder)): ColumnOrdered[_] = sort match { 
     case (field, SortOrder.Asc) => ColumnOrdered(sortMap.getOrElse(field, throw new ClientException(s"Can't sort by $field")), Ordering(Ordering.Asc)) 
     case (field, SortOrder.Desc) => ColumnOrdered(sortMap.getOrElse(field, throw new ClientException(s"Can't sort by $field")), Ordering(Ordering.Desc)) 
    } 
    def all(sort: (String, SortOrder)) = tableQuery.sortBy(sortKey(_, sort)).result 
} 

object User extends Model { 
    type ATable = Tables.User 
    val tableQuery = Tables.User 

    val sortMap = Map("id" -> tableQuery.id) 
} 

Aber db.run(User.all(("id", SortOrder.Asc)) läuft führt den folgenden Fehler:

slick.SlickException: No type for symbol name found in Vector[t2<@t3<UnassignedType>>] 

Kennt jemand eine bessere Lösung oder wohin ich gehe falsch?

Antwort

0

So wie ich dieses Problem gelöst war, meine sortMap ändern Sie den Tabellentyp mit Spaltentyp implizit (Map[String, Rep[_]]-Map[String, ATable => Rep[_]]) enthalten:

trait Model { 
    type ATable <: AbstractTable[_] 

    def tableQuery: TableQuery[ATable] 

    def sortMap: Map[String, ATable => Rep[_]] 

    private def sortKey(baseQ: Query[ATable, ATable#TableElementType, Seq], sort: (String, SortOrder)) = { 
     val rep = sortMap.getOrElse(sort._1, throw new ClientException(s"Can't sort by ${sort._1}")) 
     val orderedRep = sort._2 match { 
      case SortOrder.Asc => (t: ATable) => ColumnOrdered(rep(t), slick.ast.Ordering(slick.ast.Ordering.Asc)) 
      case SortOrder.Desc => (t: ATable) => ColumnOrdered(rep(t), slick.ast.Ordering(slick.ast.Ordering.Desc)) 
     } 
     q.sortBy(orderedRep) 
    } 

    def all(sort: (String, SortOrder)) = sortKey(tableQuery, sort).result 
} 

object User extends Model { 
    type ATable = Tables.User 
    val tableQuery = Tables.User 

    val sortMap = Map("id" -> { (t: ATable) => t.id }) 
} 

Der einzige suboptimal Aspekt dieser Lösung besteht darin, dass, wenn eine neue Spalte wird zur Datenbank hinzugefügt und muss manuell zur sortKey hinzugefügt werden. Ich überlege, diese Karten in die Tabellencode-Generierungsskripte aufzunehmen.

0

Sie müssen nur in Generation etwas zählen wie

val columns = Map("id" -> { (t: User) => t.id }) 

und es verwenden, etwa so:

class UserDAO{ 
//... 
baseQuery = filterContext.sort.foldLeft[Query[User, UserRow, Seq]](User) ((_, s) => UserColumnFilter.getSort(s,User.baseTableRow.columns)); 
//... 
} 

case class Sort(columnName:String,value: String) {} 

case class FilterContext(filters:List[Filter],page:Int,pageSize:Int,sort:List[Sort]) 
    object UserColumnSort extends ColumnSort[User,UserRow](User) 
    class ColumnSort[T<:Table[C],C](query:TableQuery[T]) 
    {  

     def getSort(s:Sort,columns:Map[String, T => Rep[_]]):Query[T, C, Seq] = { 
     val aux = columns.getOrElse(s.columnName, throw new Exception(s"Can't sort by ${s.columnName}")); 
      val orderedRep = s.value match { 
       case "asc" => (t: T) => ColumnOrdered(aux(t), slick.ast.Ordering(slick.ast.Ordering.Asc)) 
       case _ => (t: T) => ColumnOrdered(aux(t), slick.ast.Ordering(slick.ast.Ordering.Desc)) 
      } 
      return query.sortBy(orderedRep); 

     } 
    }