3

I'm using Slick 3.0, and following is my codes:

def registerMember(newMember: TeamMember): Future[Long] = {

  db.run(
    teamProfileTable.filter(u => u.ID === newMember.ID).result.headOption
  ).flatMap {
    case None => Future(-1)
    case _ => db.run(
      (teamProfileTable returning teamProfileTable.map(_.staffID)) += newMember.toTeamRecord
    )
  }
}

This may look ok. But when there are more layers of callback, the codes may become hard to read. I tried to simplify the codes using for-expression or andThen.. But due to the pattern matching part, I can only use flatMap to implement this..

Does anyone have ideas about how to refactor this?

  • You should combine the queries and then only as the last step run the composition on the db. This is how I think it's supposed to work. If you add some more code to make a runnable example I could give it a try with an answer. – pagoda_5b May 12 '15 at 10:00
  • @pagoda_5b Thanks! But how to combine the two queries if the second query depends on the result of first query? I think `andThen` is not suitable here.. – Hanfei Sun May 12 '15 at 10:03
  • @hanfeisun You should probably use for comprehensions, but I can't help you further if you can't provide some self-consistent code to test. I may only guess the intent and the result of your code unless you're more explicit. :) – pagoda_5b May 18 '15 at 08:07

2 Answers2

2

I think a for comprehension should be okay here, you just need conditional handling of the Option in the result of the first Future. Something like this should work (note I did not compile check this):

def registerMember(newMember: TeamMember): Future[Long] = {

  for{
    r1Opt <- db.run(teamProfileTable.filter(u => u.ID === newMember.ID).result.headOption
    r2 <- r1Opt.fold(Future.successful(-1L))(r1 => db.run((teamProfileTable returning teamProfileTable.map(_.staffID)) += newMember.toTeamRecord)
  } yield r2

}

You can see on the right side of the fold that I have access to the result of the first Future if it was a Some (as r1).

I would even take this a step further and create separate methods for the steps of the for comprehension to clean things up, like so:

def registerMember(newMember: TeamMember): Future[Long] = {
  def findMember = 
    db.run(teamProfileTable.filter(u => u.ID === newMember.ID).result.headOption

  def addMember(r1Opt:Option[TeamMember]) = {
    r1Opt.fold(Future.successful(-1L)){r1 =>
      db.run((teamProfileTable returning teamProfileTable.map(_.staffID)) += 
        newMember.toTeamRecord)
    }
  }

  for{
    r1Opt <- findMember
    r2 <- addMember(r1Opt)
  } yield r2

}
cmbaxter
  • 35,283
  • 4
  • 86
  • 95
0

Another approach to simplify nested db.runs in Slick 3.0 when the query spans two tables could be to join the queries into a single query. Joining and Zipping. However, the OP seems to have the somewhat rarer case of nested queries on the same table so this approach may not be helpful in that particular case.

val query = slickLoginInfos join slickUserLoginInfos on 
   ((l,ul) => l.id === ul.loginInfoId) 
db.run((for { (l, ul) <- query } yield (ul)).result.headOption)
Sully
  • 494
  • 1
  • 5
  • 12