I have a database for objects called Campaigns containing three fields :
- Id (int, not nullable)
- Version (int, not nullable)
- Stuff (Text, nullable)
Let's call CampaignsRow the corresponding slick entity class
When I select line from Campaigns, I don't always need to read stuff, which contains big chunks of text.
However, I'd very much like to work in the codebase with the class CampaignsRow instead of a tuple, and so to be able to sometimes just drop the Stuff column, while retaining the original type
Basically, I'm trying to write the following function :
//Force dropping the Stuff column from the current Query def smallCampaign(campaigns: Query[Campaigns, CampaignsRow, Seq]): Query[Campaigns, CampaignsRow, Seq] = { val smallCampaignQuery = campaigns.map { row => CampaignsRow(row.id, row.version , None : Option[String]) } smallCampaignQuery /* Fails because the type is now wrong, I have a Query[(Rep[Int], Rep[Int], Rep[Option[String]), (Int, Int, Option[String], Seq] */ } Any idea how to do this ? I suspect this has to do with Shape in slick, but I can't find a resource to start understanding this class, and the slick source code is proving too complex for me to follow.
2 Answers
Answers 1
You're actually already doing almost what you want in def *, the default mapping. You can use the same tools in the map method. Your two tools are mapTo and <>.
As you've found, there is the mapTo method which you can only use if your case class exactly matches the shape of the tuple, so if you wanted a special case class just for this purpose:
case class CampaignLite(id: Int, version: Int) val smallCampaignQuery = campaigns.map { row => (row.id, row.version).mapTo[CampaignLite] } As you want to reuse your existing class, you can write your own convert functions instead of using the standard tupled and unapply and pass those to <>:
object CampaignRow { def tupleLite(t: (Int, Int)) = CampaignRow(t._1, t._2, None) def unapplyLite(c: CampaignRow) = Some((c.id, c.version)) } val smallCampaignQuery = campaigns.map { row => (row.id, row.version) <> (CampaignRow.tupleLite, CampaignRow.unapplyLite) } This gives you the most flexibility, as you can do whatever you like in your convert functions, but it's a bit more wordy.
As row is an instance of the Campaigns table you could always define it there alongside *, if you need to use it regularly.
class Campaigns ... { ... def * = (id, version, stuff).mapTo[CampaignRow] def liteMapping = (id, version) <> (CampaignRow.tupleLite, CampaignRow.unapplyLite) } val liteCampaigns = campaigns.map(_.liteMapping) Reference: Essential Slick 3, section 5.2.1
Answers 2
If I understand your requirement correctly, you could consider making CampaignRow a case class that models your Campaigns table class by having Campaigns extend Table[CampaignRow] and providing the bidirectional mapping for the * projection:
case class CampaignRow(id: Int, version: Int, stuff: Option[String]) class Campaigns(tag: Tag) extends Table[CampaignRow](tag, "CAMPAIGNS") { // ... def * = (id, version, stuff) <> (CampaignRow.tupled, CampaignRow.unapply) } You should then be able to do something like below:
val campaigns = TableQuery[CampaignRow] val smallCampaignQuery = campaigns.map( _.copy(stuff = None) ) For a relevant example, here's a Slick doc.
0 comments:
Post a Comment