Search code examples
mongodbscalascalatrasalat

How to get None Option from salatDAO when mongodb gives error


import com.escalatesoft.subcut.inject._
import com.mongodb.casbah.Imports._
import com.novus.salat._
import com.novus.salat.global._
import com.novus.salat.dao._

case class User(_id: ObjectId = new ObjectId, email: String, password: String)

class UserDAO(coll: MongoCollection = DatabaseClient.getCollection("users")) extends SalatDAO[User, ObjectId](
  collection = coll
)

class UserRepository(implicit val bindingModule: BindingModule) extends Injectable {
  val userDAO = injectOptional [UserDAO] getOrElse {new UserDAO}

  def createUser (email: String, password: String):Option[ObjectId] = {
    val newUser = User(email = email, password = password)
    val createdUser = userDAO.insert(newUser)
    createdUser
  }
}

Basically When inserting a new user it returns the Some("ObjectId of new user") which is exactly what I expect it to do. However when I put an Index on the email then I get a duplicate key error. What I would like is instead of getting the duplicate key error, getting the None option just like I do when I read from the collection and there is no matching document.

How can I get a None option when MongoDB returns the duplicate key error?

Or how should I be handling this error that I get back?


Solution

  • I'll give you two answers to this problem. The first one requires a slight change your approach. If the Salat DAO functionality is potentially throwing exceptions on insert, you might want to consider changing the createUser function to return a Try[Option[ObjectId]] instead and rework it like so:

    def createUser (email: String, password: String):Try[Option[ObjectId]] = {
      val newUser = User(email = email, password = password)
      Try(userDAO.insert(newUser))  
    }
    

    Now the caller knows that the result will be one of three things: a Success(Some(objectId)), a Success(None) (not sure when this will happen but since it's an Option, you have to be able to handle it) or a Failure wrapping some exception. This way you could even pattern match on the exception in the Failure to make sure it's the one thrown on duplicate key and act accordingly as opposed to just swallowing any exception and assuming it must have been due to duplicate key.

    Now if you really want a None for any failure, you could just redefine createUser like this:

    def createUser (email: String, password: String):Option[ObjectId] = {
      val newUser = User(email = email, password = password)
      Try(userDAO.insert(newUser)).toOption.flatten 
    } 
    

    This will swallow any exception from insert and return a None.