Đây là một giải pháp cho Slick 3.2.3 (và một số thông tin cơ bản về cách tiếp cận của tôi):
Bạn có thể nhận thấy động chọn cột dễ dàng miễn là bạn có thể giả sử một loại cố định, ví dụ:
columnNames = List("col1", "col2")
tableQuery.map( r => columnNames.map(name => r.column[String](name)) )
Nhưng nếu bạn hãy thử một cách tiếp cận tương tự
với một groupBy
hoạt động, Slick sẽ phàn nàn rằng nó "does not know how to map the given types"
.
Vì vậy, mặc dù đây không phải là một giải pháp thanh lịch, nhưng ít nhất bạn có thể đáp ứng mức độ an toàn về kiểu chữ của Slick bằng cách xác định tĩnh cả hai:
-
groupby
loại cột - Giới hạn trên / dưới về số lượng
groupBy
cột
Một cách đơn giản để thực hiện hai ràng buộc này là một lần nữa giả sử một kiểu cố định và phân nhánh mã cho tất cả các số lượng có thể có của groupBy
cột.
Đây là phiên Scala REPL đầy đủ hoạt động để cung cấp cho bạn ý tưởng:
import java.io.File
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
import slick.jdbc.H2Profile.api._
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
val confPath = getClass.getResource("/application.conf")
val config = ConfigFactory.parseFile(new File(confPath.getPath)).resolve()
val db = Database.forConfig("slick.db", config)
implicit val system = ActorSystem("testSystem")
implicit val executionContext = system.dispatcher
case class AnyData(a: String, b: String)
case class GroupByFields(a: Option[String], b: Option[String])
class AnyTable(tag: Tag) extends Table[AnyData](tag, "macro"){
def a = column[String]("a")
def b = column[String]("b")
def * = (a, b) <> ((AnyData.apply _).tupled, AnyData.unapply)
}
val table = TableQuery[AnyTable]
def groupByDynamically(groupBys: Seq[String]): DBIO[Seq[GroupByFields]] = {
// ensures columns are returned in the right order
def selectGroups(g: Map[String, Rep[Option[String]]]) = {
(g.getOrElse("a", Rep.None[String]), g.getOrElse("b", Rep.None[String])).mapTo[GroupByFields]
}
val grouped = if (groupBys.lengthCompare(2) == 0) {
table
.groupBy( cols => (cols.column[String](groupBys(0)), cols.column[String](groupBys(1))) )
.map{ case (groups, _) => selectGroups(Map(groupBys(0) -> Rep.Some(groups._1), groupBys(1) -> Rep.Some(groups._2))) }
}
else {
// there should always be at least one group by specified
table
.groupBy(cols => cols.column[String](groupBys.head))
.map{ case (groups, _) => selectGroups(Map(groupBys.head -> Rep.Some(groups))) }
}
grouped.result
}
val actions = for {
_ <- table.schema.create
_ <- table.map(a => (a.column[String]("a"), a.column[String]("b"))) += ("a1", "b1")
_ <- table.map(a => (a.column[String]("a"), a.column[String]("b"))) += ("a2", "b2")
_ <- table.map(a => (a.column[String]("a"), a.column[String]("b"))) += ("a2", "b3")
queryResult <- groupByDynamically(Seq("b", "a"))
} yield queryResult
val result: Future[Seq[GroupByFields]] = db.run(actions.transactionally)
result.foreach(println)
Await.ready(result, Duration.Inf)
Điều này trở nên tồi tệ đến đâu khi bạn có thể có một vài groupBy
các cột (nghĩa là có một if
riêng biệt nhánh cho 10 trường hợp trở lên sẽ trở nên đơn điệu). Hy vọng rằng ai đó sẽ tìm thấy và chỉnh sửa câu trả lời này để biết cách ẩn bảng soạn sẵn đó đằng sau một số đường cú pháp hoặc lớp trừu tượng.