> (for {
> t <- Tactics if t.brandId === request.user.brandId
> m <- Mediums if
m.id === t.mediumId
> g <- Geographies if
g.id === t.geographyId
> p <- Participants if
p.id === t.participantId
> tar <- Targets if
tar.id === t.targetId
> } yield (
t.id,
t.name,
m.name,
g.name,
p.name,
tar.name, t.insertDate.?)
> ).list.map(
> t => TacticSummary(t._1, t._2, t._3, List(t._4, t._5,
> t._6).mkString(", "), t._7)
> )
>
> It's ugly because the tuple is getting pretty big and remember what each
> _N is is tricky.
How about doing the concatenation on the server side?
for ... yield (
t.id,
t.name,
m.name,
g.name.asColumnOf[String] + ", " +
p.name.asColumnOf[String] + ", " +
tar.name.asColumnOf[String],
t.insertDate.?)
).list.map(TacticSummary.tupled)
Depending on your types, you may be able to leave out some or all of the
asColumnOf calls.
This breaks down, when you cannot express your computation as a
server-side operation. In that case the limits of Scala force you into
numeric column identifiers, instead of column names. There is research
going on in EPFL and other places (e.g. shapeless' record types) to
overcome this limitation somehow, but this is work in progress and it is
unclear if or how well it could be integrated with Slick.
Alternatively, you can what we call a "custom shape" to teach Slick
structured types other than the built-in tuples and hlist. You can use
this to allow using a custom case class within your query. Here is a
"quick" example, which I will probably polish for the 2.0.1 docs. It
requires advanced Slick and Scala knowledge to understand.
import scala.slick.lifted.{ProductNodeShape, Shape, ShapeLevel}
// A custom case class
case class BRow(a: Int, b: String)
case class BProjection(a: Column[Int], b: Column[String])
// A Shape that tells Slick about the mapping between BProjection and BRow
final class PairShape[Level <: ShapeLevel, M <: BProjection, U <: BRow,
P <: BProjection](val shapes: Seq[Shape[_, _, _, _]]) extends
MappedScalaProductShape[Level, Product, M, U, P] {
def buildValue(elems: IndexedSeq[Any]) = {
def toWhatever[T](t:Any) = t.asInstanceOf[T]
BProjection(toWhatever(elems(0)), toWhatever(elems(1)))
}
def copy(shapes: Seq[Shape[_, _, _, _]]) = new PairShape(shapes)
override def toMapped(v: Any) = {
val elems = v.asInstanceOf[Product].productIterator.toIndexedSeq
new BRow(elems(0).asInstanceOf[Int], elems(1).asInstanceOf[String])
}
}
implicit def pairShape[Level <: ShapeLevel, M1, M2, P1, P2] =
new PairShape[Level, BProjection, BRow, BProjection](Seq(
implicitly[Shape[ShapeLevel.Flat,Column[Int],Int,Column[Int]]],implicitly[Shape[ShapeLevel.Flat,Column[String],String,Column[String]]]
))
// Use it in a table definition
class B(tag: Tag) extends Table[BRow](tag, "shape_a") {
def id = column[Int]("id", O.PrimaryKey)
def s = column[String]("s")
def all = (id, s)
def * = BProjection(id, s)
}
val bs = TableQuery[B]
bs.ddl.create
// Insert data with the custom shape
bs += BRow(1, "a")
bs.map(_.all) += ((2, "c"))
bs += BRow(3, "b")
// Use it for returning data from a query
val q3 = bs
.map { case b => BProjection(
b.id, (b.s ++ b.s)) }
.filter { case BProjection(id, _) => id =!= 1 }
.sortBy { case BProjection(_, ss) => ss }
//.map { case Pair(id, ss) => Pair(id, Pair(42 , ss)) }
assertEquals(Vector(BRow(3,"bb"), BRow(2,"cc")), q2.run)
Chris