В моем текущем проекте Play2 я реализовал ReactiveMongo для хранения своих пользовательских объектов.
Пользователь:
case class User(override var _id: Option[BSONObjectID] = None,
override var created: Option[DateTime] = None,
override var updated: Option[DateTime] = None,
firstName: String,
lastName: String,
email: String,
emailValidated: Boolean,
phoneNumber: String,
lastLogin: DateTime,
linkedProviders: Seq[LinkedProvider],
userRoles: Seq[UserRole.UserRole]) extends TemporalModel {
}
import EnumUtils.enumReads
implicit val userRoleReads = enumReads(UserRole)
import mongo_models.LinkedProvider.linkedProviderReads
implicit val userReads: Reads[User] = (
(__ \ "_id").read[Option[BSONObjectID]] and
(__ \ "created").read[Option[DateTime]] and
(__ \ "updated").read[Option[DateTime]] and
(__ \ "firstName").read[String] and
(__ \ "lastName").read[String] and
(__ \ "email").read[String] and
(__ \ "emailValidated").read[Boolean] and
(__ \ "phoneNumber").read[String] and
(__ \ "lastLogin").read[DateTime] and
(__ \ "linkedProviders").read(list[LinkedProvider](linkedProviderReads)) and
(__ \ "userRoles").read(list[UserRole.UserRole])
)(User.apply _)
import EnumUtils.enumWrites
import mongo_models.LinkedProvider.linkedProviderWrites
import play.api.libs.json.Writes._
implicit val userWrites: Writes[User] = (
(__ \ "_id").write[Option[BSONObjectID]] and
(__ \ "created").write[Option[DateTime]] and
(__ \ "updated").write[Option[DateTime]] and
(__ \ "firstName").write[String] and
(__ \ "lastName").write[String] and
(__ \ "email").write[String] and
(__ \ "emailValidated").write[Boolean] and
(__ \ "phoneNumber").write[String] and
(__ \ "lastLogin").write[DateTime] and
(__ \ "linkedProviders").write(Writes.traversableWrites[LinkedProvider](linkedProviderWrites)) and
(__ \ "userRoles").write(Writes.traversableWrites[UserRole.UserRole])
)(unlift(User.unapply))
LinkedProvider:
case class LinkedProvider(userId: String,
providerId: String,
authMethod: String,
avatarUrl: String,
oAuth1Info: Option[OAuth1Info] = None,
oAuth2Info: Option[OAuth2Info] = None,
passwordInfo: Option[PasswordInfo] = None) {
}
object LinkedProvider {
implicit val o1 = Json.format[OAuth1Info]
implicit val o2 = Json.format[OAuth2Info]
implicit val p = Json.format[PasswordInfo]
implicit val linkedProviderReads: Reads[LinkedProvider] = (
(__ \ "userId").read[String] and
(__ \ "providerId").read[String] and
(__ \ "authMethod").read[String] and
(__ \ "avatarUrl").read[String] and
(__ \ "oAuth1Info").read[Option[OAuth1Info]] and
(__ \ "oAuth2Info").read[Option[OAuth2Info]] and
(__ \ "passwordInfo").read[Option[PasswordInfo]]
)(LinkedProvider.apply _)
implicit val linkedProviderWrites: Writes[LinkedProvider] = (
(__ \ "userId").write[String] and
(__ \ "providerId").write[String] and
(__ \ "authMethod").write[String] and
(__ \ "avatarUrl").write[String] and
(__ \ "oAuth1Info").write[Option[OAuth1Info]] and
(__ \ "oAuth2Info").write[Option[OAuth2Info]] and
(__ \ "passwordInfo").write[Option[PasswordInfo]]
)(unlift(LinkedProvider.unapply))
}
Как видите, я реализовал операции чтения и записи, чтобы объекты правильно сохранялись в экземпляре mongoDB. При вставке с новым объектом все работает как часы, и структура объекта правильно сохраняется и извлекается из mongoDB.
У меня проблемы с выяснением того, как обрабатывать обновления. Допустим, я хочу обновить некоторые значения моего пользовательского объекта следующим образом:
val userForUpdate = user.copy(
firstName = identity.firstName,
lastName = identity.lastName,
email = identity.email.getOrElse(""),
lastLogin = DateTime.now()
)
А затем вызовите мой метод обновления:
UserDAO.update(user._id.get.stringify, userForUpdate, false)
Метод обновления:
def update(id: String, document: T, upsert: Boolean)(implicit writer: Writes[T]): Future[Either[ServiceException, T]] = {
document.updated = Some(new DateTime())
Logger.debug(s"Updating document: [collection=$collectionName, id=$id, document=$document]")
Recover(collection.update(DBQueryBuilder.id(id), DBQueryBuilder.set(document))) {
document
}
}
Метод DBQueryBuilder.set():
def set[T](data: T)(implicit writer: Writes[T]): JsObject = Json.obj("$set" -> data)
Это вызовет эту ошибку:
[error] application - DB operation failed: [message=DatabaseException['Mod on _id not allowed' (code = 10148)]]
[error] application - DatabaseException: [code=10148, isNotAPrimaryError=false]
так как в записи (__ \ "_id").write[Option[BSONObjectID]]
указано, что поле _id также должно быть сериализовано при вызове метода DBQueryBuilder.set(). Обновление _id, как мы знаем, не разрешено и определенно не должно выполняться в этом случае.
Итак, мой вопрос: как мне справиться с этим? Я предполагаю, что есть какой-то умный способ Scala, который не требует написания всего "$set" -> запроса? Может быть, создать лучший DBQueryBuilder? Может быть, определить другое определение записи?
Пожалуйста, дайте свой лучший шанс и помните, что я новичок в Scala, так что будьте осторожны =)!