Oleg Zinoviev
Oleg Zinoviev

Reputation: 559

Future and Option in for comprehension in slick

I pretty new in using slick and now I faced with the issue how to retrieve some data from two tables. I have one table

class ExecutionTable(tag: Tag) extends Table[ExecTuple](tag, "execution") {
  val id: Rep[String] = column[String]("id")
  val executionDefinitionId: Rep[Long] = column[Long]("executionDefinitionId")
  // other fields are omitted

  def * = ???
}

and another table

class ServiceStatusTable(tag: Tag)
  extends Table[(String, Option[String])](tag, "serviceStatus") {
  def serviceId: Rep[String] = column[String]("serviceId")
  def detail: Rep[String] = column[String]("detail")

  def * = (serviceId, detail.?)
}

In Dao I convert data from this two tables to a business object

case class ServiceStatus(
  id: String, 
  detail: Option[String] = None, //other fields
)

like this

private lazy val getServiceStatusCompiled = Compiled {
  (id: Rep[String], tenantId: Rep[String]) =>
  for {
    exec   <- getExecutionById(id, tenantId)
    status <- serviceStatuses if exec.id === status.serviceId
  } yield mapToServiceStatus(exec, status)
}

and later

def getServiceStatus(id: String, tenantId: String)
: Future[Option[ServiceStatus]] = db
  .run(getServiceStatusCompiled(id, tenantId).result.transactionally)
  .map(_.headOption)

The problem is that not for all entries from table execution exists entry in table serviceStatus. I cannot modify table execution and add to it field details as it is only service specific. When I run query in case when for entry from execution exists entry in serviceStatus all works as expected. But if there is no entry in serviceStatus, Future[None] is returned. Question: Is there any option to obtain status in for comprehension as Option depending on existing entry in table serviceStatus or some else workaround?

Upvotes: 0

Views: 365

Answers (1)

Nyavro
Nyavro

Reputation: 8866

Usually, in case when join condition does not find corresponding record in the "right" table but the result should still contain the row from "left" table, left join is used. In your case you can do something like:

Execution
  .filter(...execution table filter...)
  .joinLeft(ServiceStatus).on(_.id===_.serviceId)

This gives you pair of

(Execution, Rep[Option[ServiceStatus]]) 

and after query execution:

(Execution, Option[ServiceStatus])      

Upvotes: 1

Related Questions