0

所以,我有一個case類以及JSON和BSONDocument格式的讀者和作者。使用case類進行json驗證和MongoDB persistense(Reactivemongo),那麼id呢?

問題是,當插入到MongoDB時,我想能夠指定BSONObjectID,所以我可以在創建時返回它。但是,如果我在case類中添加id: BSONObjectID,我無法找到讓JSON驗證/轉換正常工作的方法。

這是我的代碼:

case class Mini(username: String, email: String, quizAnswer1: List[String]) 

implicit object MiniWriter extends BSONDocumentWriter[Mini] { 
    def write(mini: Mini): BSONDocument = BSONDocument(
    "username" -> mini.username, 
    "email" -> mini.email, 
    "quizAnswer1" -> mini.quizAnswer1 
) 
} 

implicit object MiniReader extends BSONDocumentReader[Mini] { 
    def read(doc: BSONDocument): Mini = Mini(
    doc.getAs[String]("username").get, 
    doc.getAs[String]("email").get, 
    doc.getAs[List[String]]("quizAnswer1").toList.flatten 
) 
} 

implicit val miniReads: Reads[Mini] = (
    (JsPath \ "username").read[String] and 
    (JsPath \ "email").read[String] and 
    (JsPath \ "quizAnswer1").read[List[String]] 
)(Mini.apply _) 

implicit val miniWrites: Writes[Mini] = (
    (JsPath \ "username").write[String] and 
    (JsPath \ "email").write[String] and 
    (JsPath \ "quizAnswer1").write[List[String]] 
)(unlift(Mini.unapply)) 

我真的希望避免與相同型號的重複模型表示工作。有任何想法嗎?

回答

1

如果您不需要在模型本身的ID,你可以只使用它暫時你的操作。在一個讓你可以使用以下基本實現:

def insert(t: T)(implicit ctx: ExecutionContext): Future[BSONObjectID] = { 
    val id = BSONObjectID.generate 
    val obj = format.writes(t).as[JsObject] 
    obj \ "_id" match { 
     case _: JsUndefined => 
     coll.insert(obj ++ Json.obj("_id" -> id)).map { _ => id } 

    case JsObject(Seq((_, JsString(oid)))) => 
     coll.insert(obj).map { _ => BSONObjectID(oid) } 

    case JsString(oid) => 
     coll.insert(obj).map { _ => BSONObjectID(oid) } 

    case f => sys.error(s"Could not parse _id field: $f") 
} 

}

上的ID是由PO​​ST請求的最新情況。

當你查詢數據庫,你可以使用下面的基本實現,以獲得結果集臨時

def find(sel: JsObject, limit: Int = 0, skip: Int = 0, sort: JsObject = Json.obj(), projection: JsObject = Json.obj())(implicit ctx: ExecutionContext): Future[Traversable[(T, BSONObjectID)]] = { 
    val cursor = coll.find(sel).projection(projection).sort(sort).options(QueryOpts().skip(skip).batchSize(limit)).cursor[JsObject] 
    val l = if (limit != 0) cursor.collect[Traversable](limit) else cursor.collect[Traversable]() 
    l.map(_.map(js => (js.as[T], (js \ "_id").as[BSONObjectID]))) 
} 
+0

謝謝,該答案使我朝着正確的方向發展。我也將Option放入最終解決方案中,用於其他非強制性領域。 – Wrench

0

使用播放,您可以使用劇中的JSON fomrat /讀/直接寫:

https://www.playframework.com/documentation/2.3.x/ScalaJsonCombinators

一個例子看起來像:

object Mini { 
    implicit val miniFormat: Format[Mini] = Json.format[Mini] 
} 

據我所知,你必須申報id爲'_id'的對象。

case class Mini(_id:BSONObjectId, username: String, email: String, quizAnswer1: List[String]) 

對於您可以使用以下格式BSONObjectId隱含的皈依:

implicit object BSONObjectIDFormat extends Format[BSONObjectID] { 
def writes(objectId: BSONObjectID): JsValue = { 
    Json.obj("$oid" -> JsString(objectId.stringify)) 
} 
def reads(json: JsValue): JsResult[BSONObjectID] = json match { 
    case JsString(x) => { 
    val maybeOID: Try[BSONObjectID] = BSONObjectID.parse(x) 
    if (maybeOID.isSuccess) JsSuccess(maybeOID.get) else { 
     JsError("Expected BSONObjectID as JsString") 
    } 
    } 

    case JsObject(Seq((_, oid))) => 
    reads(oid) 

    case _ => JsError("Expected BSONObjectID as JsString") 
} 

}

+0

我覺得我結束了使用兩個班的情況下,一個與_id供內部使用的ID ,在創建時添加_id,另一個暴露給API。原因在於,在執行PUT(創建)或POST(更新)時,不應提供_id。在一個PUT中它被創建並返回。在POST中,_id來自URL路徑,如'/ mini/a675c76d576d5c'。因此,我將生成的BSONObjectID添加到json結構中,並使用內部case類和它的寫入進行驗證,然後使用BSONDocument編寫器對_id的內部版本進行最終轉換。 – Wrench

+0

如果你不需要模型本身的id,你可以在操作時臨時使用它。在PUT上,您可以使用以下基本實現: – toggm