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?
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"))
}
}
}