2017-02-22 13 views
2

私はスケーラ、playframeworkと滑らかなを学んでいますが、私は問題を発見しました。 カスタムフィルタフィールド、ページネーション情報(ページサイズと番号)、フィールド名と順序(ascまたはdesc)を持つ文字列タプルのSeqを受け取るリストコントローラを使用して、単純なCRUDを作成しようとしています。 seqによる注文を除いて、すべてが正常に動作しています。私は動的に注文を行うことはできません。いくつかの列を持つ滑らかなスカラの動的順序

私はScadiddle blogから基本構造を得ました。次のように だから、基本的なコードは次のとおりです。

私は私の基本的なカラーモデルを持っている:私は検索方法を持っている私のレポで

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

case class Color(
    id: Int, 
    name: String) 

これは、単純なテーブル定義であります:

def findAll(searchTerm: Option[String], page: Int, top: Int, sortsBy: Seq[(String, SortDirection)]): Future[Seq[Color]] = { 
    var query = searchTerm match { 
     case Some(term) => colors.filter(_.name like s"%$term%") 
     case _ => colors 
    } 
    val offset = (page - 1) * top 
    // This is building the sort clause, matching on each column in the sort 
    sortsBy.foreach { 
     sortTuple => 
     val (sortColumn, sortDirection) = sortTuple 
     query = query.sortBy(sortColumn match { 
      case "id" => if (sortDirection == Desc) _.id.desc else _.id.asc 
      case _ => if (sortDirection == Desc) _.name.desc else _.name.asc 
     }) 
    } 
    // The "list" method actually executes the query 
    val colorsQuery = query.drop(offset).take(top).result 

    db.run(colorsQuery) 
    } 

問題がこのシーケンスの検索方法を呼び出すと、

val sortsBy = Seq[(String, SortDirection)](("name", Desc), ("id", Asc)) 
    colorService.getColors(None, 1, 10, sortsBy).map(colorList => Ok(Json.toJson(colorList))) 

このクエリが生成されます:あなたが見ることができるように、SORTBYの順序が反転しまっ

select "id", "name" from "color" order by "id", "name" desc limit 10 offset 0 

(idと、名前、代わりにシーケンスとして名前とidの)。

私が代わりにforeachの順序が尊重されたタプルを使用する場合:

query = query.sortBy(
     s => (s.name.desc, s.id.asc) 
    ) 

をしかし動的なサイズタプルを生成する方法はありません。いくつかを追加するにはより多くのトラブルが滑らかなドキュメントのthis partである私の原因となる他の事をconfusionm:

は、複数の列を持つことにより、単一のORDERが複数の.sortByに相当 ではないことが、単一.sortByに呼び出すことに注意してください タプルを渡す

実際には、foreachを使用して注文を連結できますか?または、この順序が逆転するという制限があるからですか?

タプルだけがsortByに使用できる場合は、どうすれば動的サイズのオーダーを達成できますか?

はPD:悪い英語

EDITための注意をありがとうと申し訳ありません:迅速な対応のための

おかげで、私はあなたのコードを試してみましたが、正常に見える、悲しいことに、私はほとんどないアイデアを持っていますそれはどのように働く:((scalaはとてもいいですが、言語を学ぶのは難しいです:S)。

私は驚いたばかりですが、答えを探すとthisはわかりませんでした。私がScala Programmingの第3版を完成させたらうまくいけばうれしく思います。地獄が続いている。

さらにもう1つの質問ですが、これはいくつかのsortBy呼び出しを行うのと同じですか?これはどのようにタプルを使うのでしょうか?私は今でも滑らかなドキュメントのこの部分で混乱しています:複数の列を持つ

単一ORDER BYが複数の.sortByと同等ではありませんが、私がチェック

タプルを渡し単一.sortByコールではなく呼び出し、私のメソッドを追加し、seqの逆を追加することで、うまく機能するようになります。もちろん、の機能はであり、あなたのコードのようにうまくいきます。私はあなたの提案を使用し、ヘルパーでフィルタの残りの部分を作成し、 。 (他の部分では、私はまだvarを使っていますが、Scalaの詳細を理解するときにはそれをより良くしていきます)。

告白:私は繰り返す必要がある、いくつかの言語(JavaScriptからJava、C#、Pythonなど)で8年以上プログラミングした後、Scalaは美しく複雑な言語のように見えますが、それ

答えて

3

のはDynamicSortBySupportヘルパー

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) 
     } 
    } 
} 

を定義しようと "ソートマップ"

を追加し、あなたの Tableを再定義
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) 
) 
} 

、最終的にあなたのコード内で全部を使用します。

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) 
    } 
} 

ノートとコメント:文字列名とテーブル列の間

  • ランタイムマップMap[String, Rep[_]]をして改善することができましたエラー処理(これで、適切に管理されなければならないランタイム例外がスローされる)またはテーブル定義自体からの自動派生;
  • 私はを適切なslick.ast.Ordering.Directionと置き換えました。
  • filterOptionのようなオプションのフィルタのヘルパーを書くこともできます。
  • ソート順序を逆にするためにfoldRightを使用することに注意してください。
  • あなたがデータ層の外側ColumnSelectorを持つようにしたい場合は、機能:-)
  • varのを避けることが可能な場合は、暗黙的にColumnSelector[T]のようなものを必要とし、それを書き換えることができ(それは良い練習実際にあります)。
+0

私はちょうどあなたのコードを試して、うまくいきました。コメントセクションが非常に限られているので、私はいくつかの観察を追加するために私の質問を編集しました – rekiem87

関連する問題