Как получить None Option от salatDAO, когда mongodb выдает ошибку

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
  }
}

В основном при вставке нового пользователя он возвращает Some("ObjectId нового пользователя"), что я и ожидаю от него. Однако, когда я добавляю индекс в электронное письмо, я получаю ошибку дублирующего ключа. Я бы хотел, чтобы вместо получения ошибки дубликата ключа я получил вариант «Нет», как я делаю, когда читаю из коллекции, и нет соответствующего документа.

Как я могу получить параметр «Нет», когда MongoDB возвращает ошибку дублирующего ключа?

Или как мне обрабатывать эту ошибку, которую я возвращаю?


person whitehead1415    schedule 10.07.2013    source источник


Ответы (2)


Я дам вам два ответа на эту проблему. Первый требует небольшого изменения вашего подхода. Если функциональность Salat DAO потенциально вызывает исключения при вставке, вы можете рассмотреть возможность изменения функции createUser, чтобы она вместо этого возвращала Try[Option[ObjectId]], и переработать ее следующим образом:

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

Теперь вызывающая сторона знает, что результатом будет одна из трех вещей: Success(Some(objectId)), Success(None) (не уверен, когда это произойдет, но, поскольку это Option, вы должны быть в состоянии обработать его) или Failure, обертывающее какое-то исключение. Таким образом, вы могли бы даже сопоставить шаблон для исключения в Failure, чтобы убедиться, что оно вызвано дубликатом ключа, и действовать соответствующим образом, а не просто проглатывать любое исключение и предполагать, что оно должно быть связано с дубликатом ключа.

Теперь, если вам действительно нужен None для любой ошибки, вы можете просто переопределить createUser следующим образом:

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

Это проглотит любое исключение из insert и вернет None.

person cmbaxter    schedule 10.07.2013

Если вам требуется, чтобы электронное письмо было уникальным, тогда ваш метод вставки должен либо перехватывать и обрабатывать только ошибку DuplicateKeyError (не все мыслимые ошибки — что, если запись не удалась полностью? Разве вы не хотите знать это?), либо полностью избегать этой ошибки, сначала проверьте, существует ли уникальный ключ.

Я думаю, что лучший подход — не заглушать ошибку, а сначала искать в коллекции, используя ваш уникальный ключ, «электронную почту», и, если вы что-то найдете, либо обновить существующего пользователя, либо просто игнорировать повторяющегося пользователя — независимо от вашего варианта использования. .

Во-вторых, если вы используете это для модульного тестирования, ваши модульные тесты должны настроить и удалить тестовую коллекцию таким образом, чтобы каждый из ваших тестовых случаев выполнялся с внешним ресурсом (коллекцией MongoDB) в известном состоянии.

Вот пример того, как Salat делает это с помощью spec2: https://github.com/novus/salat/blob/master/salat-core/src/test/scala/com/novus/salat/test/dao/SalatDAOSpec.scala

person prasinous    schedule 10.07.2013