firstOption showing error in slick 3.0 and play 2.4

325 Views Asked by At

I am integrating silhouette (a fork of securesocial) in my project as an authentication library so I am following it's example project with slick play-silhouette-slick-seed

While writing these lines I am getting lot of errors in eclipse.

def find(loginInfo: LoginInfo) = {
  DB withSession { implicit session =>
    Future.successful {
      slickLoginInfos.filter(
        x => x.providerID === loginInfo.providerID && x.providerKey === loginInfo.providerKey
      ).firstOption match {
        case Some(info) =>
          slickUserLoginInfos.filter(_.loginInfoId === info.id).firstOption match {
            case Some(userLoginInfo) =>
              slickUsers.filter(_.id === userLoginInfo.userID).firstOption match {
                case Some(user) =>
                  Some(User(UUID.fromString(user.userID), loginInfo, user.firstName, user.lastName, user.fullName, user.email, user.avatarURL))
                case None => None
              }
            case None => None
          }
        case None => None
      }
    }
  }
}

[Errors are expected since example project is written with old releases of play and slick]

I tried to migrate them so I replaced withSession with run and firstOption with headOption since I read in slick official doc that former ones have been deprecated in slick 3.0. Following are my changes but its still not working

def find(loginInfo: LoginInfo) = {
   DB run { 
     Future.successful {
        slickLoginInfos.filter(
          x => x.providerID === loginInfo.providerID && x.providerKey === loginInfo.providerKey
        ).result.headOption match {
          case Some(info) =>
            slickUserLoginInfos.filter(_.loginInfoId === info.id).result.headOption match {
              case Some(userLoginInfo) =>
                slickUsers.filter(_.id === userLoginInfo.userID).result.headOption match {
                  case Some(user) =>
                    Some(User(UUID.fromString(user.userID), loginInfo, user.firstName, user.lastName, user.fullName, user.email, user.avatarURL))
                  case None => None
                }
              case None => None
            }
          case None => None
        }
     }
  }
}

I am new to play and scala, just exploring new things everyday. Could you please help me out in fixing these errors. It would be appreciable if anybody can also explain me use of future values, how it is beneficial over normal values that we get using Await (db.run(....)) and how to parse a value inside future object since I have seen at some places they use map and some places they use onSuccess or case, its pretty much confusing. What is best way to work with future values?

1

There are 1 best solutions below

3
On

Look up joinLeft which will use Option for the right (joinRight for the left) if nothing is matched. So, you'd want something like:

val q1 = for {
  (loginInfo, userLoginInfo) <- slickLoginInfos.filter(...) joinLeft slickUserLoginInfos on (_.id === _.loginInfoId)
} yield (loginInfo, userLoginInfo)
val q2 = for {
  ((loginInfo, userLoginInfo), user) <- q1 joinLeft slickUsers.filter(...) on (_._2.loginInfoId === _.id)
} yield {
   // use loginInfo, userLoginInfo and user are optional
}

The userLoginInfo and user there will be Option to be either Some or None depending on whether there was any match. You run this q2 and headOption on it for the overall result.

Futures

Previously, you'd do something like:

dao.fetch().map(_.toString)

where dao.fetch() returns a Seq[Int]. Now, with Slick 3.0, this would be Future[Seq[Int]] meaning you simply need another .map (or flatMap):

dao.fetch().map(_.map(_.toString))

which continues doing the same as before except that it's now wrapped in a Future. There are benefits in using Futures in that you don't need to block a thread for each and every call to the database.