0

i'm writing a play 2.3 application in Scala. I use a mongoDB database and the ReactiveMongo driver. The method that i call to read/write/update the dates in the db return a Future[Option[T]]. My question is this: if i had a method that first update a document and after read the updated document i need an onComplete statement or not? For example:

def updatePasswordInfo(user: LoginUser,info: PasswordInfo): scala.concurrent.Future[Option[BasicProfile]] = {
    import LoginUser.passwordInfoFormat //import the formatter
    //the document query
    val query = Json.obj("providerId" -> user.providerId,
                         "userId" -> user.userId
                        )
    val newPassword = Json.obj("passswordInfo" -> info)// the new password
    //search if the user exists and 
    val future = UserServiceLogin.update(query, newPassword) //update the document
    for {
        user <- UserServiceLogin.find(query).one
    } yield user //return the new LoginUser

  }

Is correct o i need to use onComplete statement before use the UserServicelogin.find(query).one statement?

alberto adami
  • 729
  • 1
  • 6
  • 25

2 Answers2

1

You have conceptual error. You're not waiting for the update to complete before retrieving the user, so it might actually end up retrieving the user before the update.

The fix is really simple:

for {
  _ <- UserServiceLogin.update(query, newPassword)
  user <- UserServiceLogin.find(query).one
} yield user

Futures in a for-comprenehsion are sequecenced, so you'll be always returning the updated user.

The reason why this works, is that the for-comprehension desugars to

UserServiceLogin.update(query, newPassword).flatMap { _ =>
  UserServiceLogin.find(query).one.map { user =>
    user
  }
}

so the find method is executed only after the update method had success.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
0

You have many options to choose from:

  • andThen
  • map
  • for comprehension

With map you will get the result of the last Future that is executed in the chain. With andThen you will get the result of first Future on which you applied andThen.

For your use-case either of for or map operations are fine. I would use map like this:

def updatePasswordInfo(user: LoginUser,info: PasswordInfo): scala.concurrent.Future[Option[BasicProfile]] = {
    import LoginUser.passwordInfoFormat //import the formatter
    //the document query
    val query = Json.obj("providerId" -> user.providerId,
                         "userId" -> user.userId
                        )
    val newPassword = Json.obj("passswordInfo" -> info)// the new password
    //search if the user exists and 
    val future = UserServiceLogin.update(query, newPassword) //update the document
    future.map( x => UserServiceLogin.find(query).one )
  }

References:

[1] http://www.scala-lang.org/api/current/#scala.concurrent.Future

[2] http://docs.scala-lang.org/sips/completed/futures-promises.html

tuxdna
  • 8,257
  • 4
  • 43
  • 61