Reputation: 30317
Inside a for-comp
I need to set up some queries that will be then run in a single shot later... say, something like:
val queries = for {
_ <- query1
_ <- query2
id <- someInsertQuery
} yield id
db.run(queries.transactionally)
Tip: the queries are instances of slick.jdbc.JdbcActionComponent#ProfileAction
Now, I need to set up conditions on the first 2 queries. If a condition is met, the queries should run, nothing otherwise. So I thought:
val queries = for {
_ <- if (condition1) query1 else Query.empty
_ <- if (condition2) query2 else Query.empty
id <- someInsertQuery
} yield id
db.run(queries.transactionally)
But this doesn't work:
value flatMap is not a member of Object
[error] _ <- if (condition1) query1 else Query.empty
[error] ^
So I bet what I am trying to do is not this way. What would be the proper way to achieve this?
Update1: More details. The first item in the for-comp looks like this (considering Boris' idea):
val queries = for {
_ <- someOption.map(someRow => SomeTableQuery += someRow).geOrElse(Query.empty.result)
}
SomeTableQuery
is an instance of slick.lifted.TableQuery[SomeClass]
and someOption
is Option[SomeClass]
Upvotes: 0
Views: 429
Reputation: 4501
I think you have some misunderstanding with for-yield construction.
In the scala, for-yield is just syntactic sugar under some combinations of flatMap
, map
functions.
val queries = for {
_ <- if (condition1) query1 else Query.empty
_ <- if (condition2) query2 else Query.empty
res <- someInsertQuery
} yield res
for the compiler it is the same as:
query1.flatMap(_ => query2.flatMap(_ => someInsertQuery.map(res => res)))
if your query1
and query2
is some ProfileAction
:
val query1: ProfileAction[Int, NoStream, Effect.Write] = ???
val query2: ProfileAction[Int, NoStream, Effect.Write] = ???
so, the type of the expression if (condition1) query1 else Query.empty
is Object
, because Query.empty
has type - Query[Unit, Unit, Seq]
and the nearest common ancestor of Query
and ProfileAction
is an Object
type, which has not flatMap
function. To make your code compilable, you should make all branches of if ... else
construction having the same type which has flatMap
function, here it would be so if we call result
on Query.empty
:
val result1: FixedSqlAction[Any, NoStream, Effect.Write with Effect.Read] = if (condition1) query1 else Query.empty.result
val result2: FixedSqlAction[Any, NoStream, Effect.Write with Effect.Read] = if (condition2) query2 else Query.empty.result
Possible version of your code:
import slick.jdbc.JdbcBackend
import slick.lifted.Query
import slick.jdbc.H2Profile.api._
val db: JdbcBackend.Database = ???
val query1: ProfileAction[Int, NoStream, Effect.Write] = ???
val query2: ProfileAction[Int, NoStream, Effect.Write] = ???
val someInsertQuery: ProfileAction[Int, NoStream, Effect.Write] = ???
val condition1 = false
val condition2 = true
val queries = for {
_ <- if (condition1) query1 else Query.empty.result
_ <- if (condition2) query2 else Query.empty.result
res <- someInsertQuery
} yield res
db.run(queries.transactionally)
If your query is actually options and not just queries, you can write option composition using flatMap
for filtering empty options and use seq
for sequential execution of result queries sequence. Example, using table in slick documentation:
import slick.jdbc.H2Profile.api._
case class Coffees(tag: Tag) extends Table[(String, Double)](tag, "COFFEES") {
def name = column[String]("COF_NAME")
def price = column[Double]("PRICE")
def * = (name, price)
}
val coffees: TableQuery[Coffees] = TableQuery[Coffees]
val maybeQuery1 = Option(("Colombian", 7.99))
val maybeQuery2 = Option(("Another name", 14.59))
val maybeQuery3 = Option(("Name", 4.39))
val queries = Seq(maybeQuery1, maybeQuery2, maybeQuery3).flatMap(_.map(someRow => coffees += someRow))
val db: Database = ???
db.run(DBIO.seq(queries:_*).transactionally)
Upvotes: 1
Reputation: 30317
The best way I have been able to solve it til now is by getting the condition inside a filter
. Roughly:
val queries = for {
_ <- query1.filter(condition1)
_ <- query2.filter(condition2)
id <- someInsertQuery
} yield id
db.run(queries.transactionally)
Let me know if you have other ideas.
Upvotes: 0