简体   繁体   中英

How should I handle Filter and Futures in play2 and Scala

I'm trying to learn Futures and ReactiveMongo. In my case I have a couple of invite objects and want to filter out the ones that already exist in the db. I do not want to update or upsert the ones already in the db. Therefore I have created a filter method:

filter method:

def isAllowedToReview(invite: Invite): Future[Boolean] = {
    ReviewDAO.findById(invite.recoId, invite.invitedUserId).map {
      maybeReview => {
        maybeReview match {
          case Some(review) => false
          case None => true
        }
      }
    }
  }

DAO:

def findById(rId: Long, userId: Long): Future[Option[Review]] = findOne(Json.obj("rId" -> recoId, "userId" -> userId))

def findOne(query: JsObject)(implicit reader: Reads[T]): Future[Option[T]] = {    
   collection.find(query).one[T]
}

and then call:

val futureOptionSet: Set[Future[Option[Invite]]] = smsSet.filter(isAllowedToReview)
save the filtered set somehow...

this doesn't work since filter expects in this case Invite => Boolean but I'm sending Invite => Future(Boolean) . How would you filter and save this?

smsSet.map(sms => isAllowedToReview(sms).map(b => sms -> b)) will have type Set[Future[(Invite, Boolean)]] . You should be able to call Future.sequence to turn it into a Future[Set[(Invite, Boolean)]] . Then you can collect the results .map(_.collect{ case (sms, true) => sms}) .

So putting everything together a solution may look like this:

val futures = smsSet.map(sms => isAllowedToReview(sms).map(b => sms -> b))
val future = Future.sequence(futures)
val result = future.map(_.collect{ case (sms, true) => sms})

When you see map and sequence you may be able to refactor to:

val filteredSet = Future.traverse(smsSet){ sms => 
  isAllowedToReview(sms).map(b => sms -> b)
}.map(_.collect{ case (sms, true) => sms})

Note that instead of returning the set, you may just want to save your sms there. But the way I wrote this, all will be wrapped in a Future and you can still compose with other operations.

You could try something like this:

val revsFut = Future.sequence(smsSet.map(invite => ReviewDAO.findById(invite.recoId, invite.invitedUserId)))    
val toSave = for(revs <- revsFut) yield {
  val flatRevs = revs.flatten
  smsSet.filter{ invite =>
    flatRevs.find(review => /*Add filter code here */).isDefined
  }
}

What I'm doing here is first fetching the Set of reviews matching the the invites by mapping over the smsSet, fetching each individually and then sequencing that into one singe Future . Then, in the for-comprehension I flatten the Set of Option[Review] and then filter down the smsSet based on what's in that flatRevs Set. Since I don't know your object model, I had to leave the impl of the flatRevs.find up to you, but it should be pretty easy as that point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM