gpt4 book ai didi

scala - 带有几列的 scala slick 中的动态排序

转载 作者:行者123 更新时间:2023-12-04 17:44:06 24 4
gpt4 key购买 nike

我一直在学习 scala、playframework 和 slick,但我发现了一个问题。
我正在尝试制作一个简单的 CRUD,它带有一个接收自定义过滤器字段的列表 Controller 、一些分页信息(页面大小和数量)以及带有字段名称和顺序(asc 或 desc)的字符串元组 Seq,以及所有内容工作正常,除了按 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)
}

在我的 repo 中,我有搜索方法:
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在光滑的文档中:

Be aware that a single ORDER BY with multiple columns is not equivalent to multiple .sortBy calls but to a single .sortBy call passing a tuple



那么,实际上我可以使用 foreach 并连接订单吗?或者是因为这个限制,订单被颠倒了?

如果只能将元组用于 sortBy,我如何实现动态大小排序?

PD:感谢您的关注,对于糟糕的英语深表歉意

编辑:

感谢您的快速回复,我尝试了您的代码并且看起来不错,遗憾的是我几乎不知道它是如何工作的:((scala 是一种非常好的但很难学习的语言:S)。

当我看到 Higher Kinded Type Should be Enabled我吓坏了,正在寻找答案 this并没有给我太多希望得到一个简单的理解,希望当我完成 Scala 编程,第 3 版书时,我会对到底发生了什么有更多的理解和知识。

还有一个问题,这是否相当于进行多次 sortBy 调用?这与使用元组相比如何?我仍然对光滑文档的这一部分感到困惑:

具有多个列的单个 ORDER BY 不等同于多个 .sortBy 调用,而是等同于传递元组的单个 .sortBy 调用

我检查了我的方法并通过在 seq 中添加一个反向使其工作正常,当然不像你的代码那样功能和漂亮,所以我将使用你的建议并工作一种方法来使用助手制作其余的过滤器并避免变量。 (是的,在另一部分中,我仍在使用 var,但是当我对 Scala 了解更多时,我会使它变得更好)。

告白:在用多种语言(从 JavaScript 到 Java、C#、Python 等)编程 8 年多之后,我不得不重复一遍,Scala 看起来是一门美丽但非常复杂的语言,但我不会放弃学习它

最佳答案

让我们定义 DynamicSortBySupport helper

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[_]]可以通过错误处理(现在它只是抛出一个必须正确管理的运行时异常)或从表定义本身自动派生来改进字符串名称和表列之间的问题;
  • 我已更换 SortDirection适当的 slick.ast.Ordering.Direction ,随意编写一个转换器;
  • 您还可以为可选过滤器编写一个帮助程序,例如 filterOption ;
  • 注意使用 foldRight反转排序顺序;
  • 如果可能,避免 var s 和功能:-)
  • 如果你想拥有 ColumnSelector在数据层之外(实际上这是一个很好的做法),您可以重写它,隐式需要类似 ColumnSelector[T] 的内容。 ;
  • 关于scala - 带有几列的 scala slick 中的动态排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42383842/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com