यहां स्लिक 3.2.3 (और मेरे दृष्टिकोण पर कुछ पृष्ठभूमि) के लिए एक समाधान है:
आपने देखा होगा कि गतिशील रूप से चयन कॉलम तब तक आसान है जब तक आप एक निश्चित प्रकार मान सकते हैं, जैसे:
columnNames = List("col1", "col2")
tableQuery.map( r => columnNames.map(name => r.column[String](name)) )
लेकिन अगर आप इसी तरह का तरीका आजमाएं
एक groupBy
. के साथ ऑपरेशन, स्लिक शिकायत करेगा कि यह "does not know how to map the given types"
।
इसलिए, जबकि यह शायद ही एक सुरुचिपूर्ण समाधान है, आप कम से कम दोनों को स्थिर रूप से परिभाषित करके स्लिक की प्रकार-सुरक्षा को संतुष्ट कर सकते हैं:
groupby
स्तंभ प्रकार- ऊपरी/निचली सीमा
groupBy
. की मात्रा पर है कॉलम
इन दो बाधाओं को लागू करने का एक आसान तरीका फिर से एक निश्चित प्रकार मान लेना और groupBy
की सभी संभावित मात्राओं के लिए कोड को शाखा देना है। कॉलम।
आपको एक विचार देने के लिए यहां पूर्ण कार्यरत स्काला आरईपीएल सत्र है:
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)
जहां यह बदसूरत हो जाता है, जब आप कुछ groupBy
. से ऊपर हो सकते हैं कॉलम (अर्थात एक अलग if
. होना 10+ मामलों के लिए शाखा नीरस हो जाएगी)। उम्मीद है कि कोई इस उत्तर में झपट्टा मारेगा और संपादित करेगा कि उस बॉयलरप्लेट को कुछ वाक्यात्मक चीनी या अमूर्त परत के पीछे कैसे छिपाया जाए।