当我在 MongoDB 中更新文档时,我通常会这样进行:
- 查找当前文档并获取版本号
- 增加版本号并保存新文档
- 将旧文档保存在 vermongo 集合中
这是我的解决方案——对于代码量我很抱歉......但有必要让你理解:
trait MyDaoComponent {
private val toObjectId = Writes[String] {
id => Json.obj("_id" -> Json.obj("$oid" -> id))
}
private val toUpdateObject = (__ \ '$set).json.copyFrom(__.json.pick)
private def setVersion(version: Option[Int]) = __.json.update(
version match {
case Some(v) => (__ \ '_version).json.put(JsNumber(v))
case None => (__ \ '_version).json.prune
}
)
private def toVersioned(deleted: Boolean) = (__.json.update(
( __ \ '_id ).json.copyFrom((__ \ '_id \ '$oid).json.pickBranch) and
( __ \ '_id \ '_version).json.copyFrom((__ \ '_version).json.pick) and
(__ \ '_version).json.update(of[JsValue].map {
case JsNumber(oldVersion) => if (deleted) JsString(f"deleted:$oldVersion%.0f") else JsNumber(oldVersion)
case _ => JsNull
}) and
(__ \ '_timestamp).json.put(Json.toJson(LocalDateTime.now)) reduce) andThen
( __ \ '_id \ '$oid ).json.prune
)
...
def update(entity: A): Future[Boolean] = {
entity.id match {
case Some(id) =>
val objectId = toObjectId.writes(id)
collection.find(objectId).cursor[JsValue].headOption.flatMap { _ match {
case Some(json) =>
collection.update(
objectId,
entity.asJson.transform(
(__ \ '_id).json.prune andThen
setVersion(Some((json as (__ \ '_version).read[Int]) + 1)) andThen
toUpdateObject
).get
).flatMap { updateLastError => if (updateLastError.ok) {
version(json, false).flatMap { versionLastError =>
if (versionLastError.ok) Future.successful(updateLastError)
else Future.failed(versionLastError)
}}
else Future.failed(updateLastError)
}
case None => Future.successful(LastError(true, None, None, None, None, 0, false))
}}.transform(
success => if (success.n > 0) true else false,
failure => failure match { case e: LastError => e.code match {
case Some(11001) => DaoServiceException(e.message, Some(DUPLICATE_KEY_ON_UPDATE))
case _ => DaoServiceException(e.message, Some(DATABASE_ERROR))
}}
)
case None => Future.failed(DaoServiceException("'id' not defined", Some(MISSING_FIELD)))
}
}
private def version(document: JsValue, deleted: Boolean): Future[LastError] = {
shadowCollection.insert(document.transform(toVersioned(deleted)).get)
}
}
上面的代码有效......但我想用原子命令替换find
调用update
。我已经尝试过FindAndModify
......但是BSONDocuments
当我使用纯 JSON 时它需要(该update
方法entity
作为参数,它提供数据的 JSON 表示)。
有没有办法将 JSON 转换为 BSON 以便我可以使用FindAndModify
?