0

I use Play! Framework with ReactiveMongo plugin (versions 2.3 and 0.11 accordingly). And I have the next code which removes items from MongoDB collection and returns a number of affected items:

/**
  * Removes all subscribers by subscription id
  * 
  * @param subscriptionId id of the subscription
  * @return count of removed subscribers
  */  
def deleteAll(subscriptionId: String): Future[Int] = {
      logger.debug(s"Removing all subscribers with subscription id '$subscriptionId'...")
      val query = BSONDocument("subscriptionId" -> BSONObjectID(subscriptionId))
      subscribersCollection.remove(query) map { writeResult: WriteResult =>
        writeResult.n match {
          case subscribersRemoved if subscribersRemoved > 0 =>
            logger.debug(s"Successfully removed $subscribersRemoved subscribers with subscribtion id '$subscriptionId'")
          case _ => logger.debug(s"No subscribers with subscribtion id '$subscriptionId' were removed")
        }
        writeResult.n
      }
  }

Due to ReactiveMongo documentation, WriteResult class returned by collection's remove() method has fields such as hasError and writeErrors to indicate errors that occurred during the execution of the database query.

So what is the best and cleanest way to enhance my method in order to return a Failure from it depending on WriteResult's hasError field?

Here is rough example:

subscribersCollection.remove(query).??? match {
  case Success(writeResult) if writeResult.hasErrors => Failure(new DatabaseException(writeResult.errMsg, writeResult.code)
  case any => any
}

I.e. my method should return a Failure even if a database query returns a Success with error fields

Thanks in advance! I really would appreciate any help in order to make my code better

P.S. I have considered wrapping all of my code in Try and just throwing an exception on hasError flag set to true, but I believe that it can be done in a better way, maybe with Future's transform() method

P.P.S. For some reason code samples and demos from ReactiveMongo documentation do not handle WriteError's error fields and flags. In fact, the documentation says

If the write result actually indicates an error, the Future will be in a failed state

However, I've already seen such handling in production code in a couple of applications, so it is slightly confusing. Does it mean that this kind of handling is excessive?

cchantep
  • 9,118
  • 3
  • 30
  • 41
Alexey Sirenko
  • 452
  • 3
  • 12
  • ReactiveMongo 0.11 is quite obsolete now. The handling of `WriteResult` is documented for newer version : http://reactivemongo.org/releases/0.12/documentation/tutorial/write-documents.html – cchantep Feb 09 '17 at 10:23
  • Thank you. The migration is in plans, but for now does it mean that "no need to check for WriteResult.ok" is appliable for WriteResult of 0.11 too? – Alexey Sirenko Feb 09 '17 at 10:41
  • There cannot be a successful `Future[WriteResult]` if there is a fatal MongoDB error/failure – cchantep Feb 09 '17 at 11:03

1 Answers1

2

Use flatMap and Future.failed

do flatMap on the Future and then based on the value return Future.failed(new Exception("unexpected value"))

Lets say we have a function which returns a future of some int

def httpStatus: Future[Int] = Future { 404 }

httpStatus.flatMap { 
 case 404 => //return failed future
  Future.failed(new Exception("Bad status"))
 case value => value
}

Now your code becomes

def deleteAll(subscriptionId: String): Future[Int] = {
      logger.debug(s"Removing all subscribers with subscription id '$subscriptionId'...")
      val query = BSONDocument("subscriptionId" -> BSONObjectID(subscriptionId))
      subscribersCollection.remove(query) flatMap { writeResult: WriteResult =>
        writeResult.n match {
          case subscribersRemoved if subscribersRemoved > 0 =>
            logger.debug(s"Successfully removed $subscribersRemoved subscribers with subscribtion id '$subscriptionId'")
            Future.successful(writeResult.n)
          case status => 
             logger.debug(s"No subscribers with subscribtion id '$subscriptionId' were removed")
             Future.failed(new Exception(s"bad status exception. status: $status"))
        }

      }
  }
Nagarjuna Pamu
  • 14,737
  • 3
  • 22
  • 40