2

Here are the codes:

def find(loginInfo: LoginInfo): Future[Option[UserProfile]] = {
  val res = DB.withSession { implicit session =>
    //if loginInfo.providerID == "waylens"
    userProfileTable.filter(u => u.userName === loginInfo.providerKey).list
  }
  val size = res.size
  if (size <= 1)
    Future(res.headOption.map(userProfileRecordToUserProfile))
  else
    throw new Exception("db error")
}

def findByEmail(providerID: String, email: String): Future[Option[UserProfile]] = {
  val res = DB.withSession { implicit session =>
    //if loginInfo.providerID == "waylens"
    userProfileTable.filter(u => u.email === email && u.status === 1).list
  }
  val size = res.size
  if (size <= 1)
    Future(res.headOption.map(userProfileRecordToUserProfile))
  else
    throw new Exception("db error")
}

def findByProfileID(profileID: Long): Future[Option[UserProfile]] = {
  val res = DB.withSession { implicit session =>
    userProfileTable.filter(u => u.id === profileID).list
  }
  val size = res.size
  if (size <= 1)
    Future(res.headOption.map(userProfileRecordToUserProfile))
  else
    throw new Exception("db error")
}

These codes appeared many times, which is really annoying

   val size = res.size
      if (size <= 1)
        Future(res.headOption.map(userProfileRecordToUserProfile))
      else
        throw new Exception("db error")

Does anyone have ideas about refactoring this in Slick?

Hanfei Sun
  • 45,281
  • 39
  • 129
  • 237

2 Answers2

1

You can pretty easily use high order functions here, first let's get your queries out of the methods:

lazy val a = userProfileTable.filter(u => u.userName === loginInfo.providerKey)
lazy val b = userProfileTable.filter(u => u.email === email && u.status === 1)
lazy val c = userProfileTable.filter(u => u.id === profileID)

Note that I'm making this lazy because you don't have a session in scope yet.

Now let's create a function to execute this queries (you can make this even more generic using a type parameter):

def executeQuery(q: ⇒ : Query[UserProfiles, UserProfiles#TableElementType]): Int = {
  db.withSession { implicit session ⇒
    q.list
  }
}

Then we need to check the length:

def listToUserProfile(list: List[UserProfile]): Future[Option[UserProfile]] = {
  if (list.length <= 1)
    Future(list.headOption.map(userProfileRecordToUserProfile))
  else
     throw new Exception("db error")
}

Now you could do something like:

listToUserProfile(executeQuery(a))

There may be some error because your code is not runnable and I couldn't compile it.

Ende Neu
  • 15,581
  • 5
  • 57
  • 68
1

First create a generic method to execute the queries which either returns a future or an error using Either


def getElement[E, U, C[_]](query: Query[E, U, C]) = {
    db.withSession { implicit session =>
        query.list.headOption match {
             case Some(ele) => Right(Future(ele))
             case None => Left(new Exception("db error"))
       }
    }
  }

Now in the code, you can simply do


def find(loginInfo: LoginInfo): Future[Option[UserProfile]] = 
      getElement(userProfileTable.filter(u => u.userName === loginInfo.providerKey)).right.map(userProfileRecordToUserProfile)

Similarly for others:


def findByEmail(loginInfo: LoginInfo): Future[Option[UserProfile]] = 
      getElement( userProfileTable.filter(u => u.email === email && u.status === 1))).right.map(userProfileRecordToUserProfile)

On a side note, you are not wrapping the future over the DB call but after you get the result. DB call will be major source of blocking and not the processing of the DB result. You should look into wrapping the DB call inside the future

mohit
  • 4,968
  • 1
  • 22
  • 39