scala slick中包含多个列的动态顺序
我一直在学习
scala,playframework和光滑,但我发现了一个问题.
我正在尝试创建一个简单的CRUD,一个列表控制器接收一个自定义过滤器字段,一些分页信息(页面大小和数量)和一个字符串元组的Seq与字段名称和顺序(asc或desc),以及一切工作正常,除了seq的订单,我不能通过动态订单. 我从Scadiddle blog获得了基本结构. 我有我的基本颜色模型: case class Color( id: Int,name: String) 这是一个简单的表定义: 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) } 在我的回购中,我有搜索方法: 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))) 生成此查询: select "id","name" from "color" order by "id","name" desc limit 10 offset 0 如您所见,sortBy的顺序被反转(id然后是name,而不是name和id作为序列). 如果我使用元组而不是foreach,那么顺序就会受到尊重: query = query.sortBy( s => (s.name.desc,s.id.asc) ) 但是没有办法生成动态大小的元组.为了增加一些混乱,导致我麻烦的另一件事是this part在光滑的文档中:
那么,实际上我可以使用foreach并连接订单吗?或者是因为订单被逆转的这种限制? 如果只有元组可以用于sortBy,我怎样才能实现动态大小的顺序? PD:感谢关注并对不好的英语感到抱歉 编辑: 感谢您的快速反应,我尝试了您的代码,看起来很好,遗憾的是我几乎不知道它是如何工作的:((scala是一个非常好的,但很难学习的语言:S). 当我看到高级打字类型应该启用时,我只是吓坏了,寻找答案this并没有给我很大的希望,让我很容易理解,希望当我完成Scala编程,第3版,我将有更多的理解和知识这到底是怎么回事. 还有一个问题,这相当于做几个sortBy调用吗?如何比较使用元组?我仍然对这部分光滑的文档感到困惑: 具有多个列的单个ORDER BY不等同于多个.sortBy调用,而是等同于传递元组的单个.sortBy调用 我检查了我的方法,并通过添加一个反向seq使它工作正常,当然不像你的代码那样功能和好,所以我将使用你的建议,并努力使其余的过滤器与助手和避免瓦尔. (是的,在另一部分我仍然使用var,但是当我更多地了解Scala时,我会更好). 忏悔:经过8年多的语言编程(从JavaScript到Java,C#,Python等)我不得不重复一遍,Scala看起来像一个美丽但非常复杂的语言,但我不会放弃学习它 解决方法
让我们定义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,C]) { def dynamicSortBy(sortBy: Seq[ColumnOrdering]): Query[A,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) } } } 并重新定义你的表添加“排序图” class ColorsTable(tag: Tag) extends Table[Color](tag,"color") with DynamicSortBySupport.ColumnSelector { def id = column[Int]("id",O.AutoInc) def name = column[String]("name") def * = (id,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),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替换了SortDirection,随意写一个转换器;>您还可以为可选过滤器编写帮助程序,例如filterOption;>注意使用foldRight来反转排序顺序;>如果可能的话避免变量并且功能正常:-)>如果你想让ColumnSelector在数据层之外(实际上这是一个很好的做法),你可以重写它,需要隐式的像ColumnSelector [T]; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |