Nested document with reactive mongo and Scala
我正在尝试通过 Scala 将嵌套文档存储在 MongoDB 中。文档看起来像:
1 2 3 4 5 6 7 8 9 10 11 12 | Project { "_id": ObjectId("528547370cf6e41449003512"), "highLevelCode": NumberLong(3), "description": [ {"_id": ObjectId("528547370cf6e41449003521"), "lang":"en", "desc":"desc in English"}, {"_id": ObjectId("528547370cf6e41449003522"), "lang":"fr", "desc":"desc en francais"}], "budget": NumberLong(12345) } |
基本上我想在项目文档中存储嵌套描述,可以是多种语言。
我写的代码是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | import reactivemongo.bson._ import reactivemongo.bson.handlers.{BSONWriter, BSONReader} import reactivemongo.bson.BSONLong import reactivemongo.bson.BSONString case class LocaleText( id: Option[BSONObjectID], lang: String, textDesc: String ) object LocaleText { implicit object LocaleTextBSONReader extends BSONReader[LocaleText] { def fromBSON(document: BSONDocument): LocaleText = { val doc = document.toTraversable LocaleText( doc.getAs[BSONObjectID]("_id"), doc.getAs[BSONString]("lang").map(_.value).get, doc.getAs[BSONString]("textDesc").map(_.value).get ) } } implicit object LocaleTextBSONWriter extends BSONWriter[LocaleText] { def toBSON(localText: LocaleText) = { BSONDocument( "_id" -> localText.id.getOrElse(BSONObjectID.generate), "lang" -> BSONString(localText.lang), "textDesc" -> BSONString(localText.textDesc) ) } } } case class Project( id: Option[BSONObjectID], description: List[LocaleText], budget: Option[Long] ) object Project { implicit object ProjectReader extends BSONReader[Project]{ def fromBSON(doc: BSONDocument): Project = { val document = doc.toTraversable Project( document.getAs[BSONObjectID]("_id"), document.getAs[BSONArray]("description").map { values => values.values.toList.flatMap { case value => value match { case v: LocaleText => Some(v.asInstanceOf[LocaleText]) case _ => None } } }.getOrElse(List.empty), document.getAs[BSONLong]("budget").map(_.value) ) } } implicit object ProjectWriter extends BSONWriter[Project]{ def toBSON(project: Project): BSONDocument = { BSONDocument( "_id" -> project.id.getOrElse(BSONObjectID.generate), "description" -> BSONArray(project.description) ).append(Seq( project.budget.map(b =>"budget" -> BSONLong(b)) ).flatten:_*) } } } |
但是,它给了我像
这样的编译错误
重载方法值适用于替代方案:[错误](生产者:reactivemongo.bson.Implicits.Producer[(String,reactivemongo.bson.BSONValue)],生产者:reactivemongo.bson.Implicits.Producer[(String,reactivemongo. bson.BSONValue)])reactivemongo.bson.AppendableBSONDocument
[错误] (els: (String, reactivemongo.bson.BSONValue))reactivemongo.bson.AppendableBSONDocument
[错误] 不能应用于 ((String, reactivemongo.bson.BSONObjectID), List[LocaleText])...
基本上 Scala 不喜欢这条线
"description" -> BSONArray(project.description)
但是,尽管我不能使用列表/数组来允许两种以上的语言,但以下替代方法仍然有效:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | case class LocaleText( enDesc: String, frDesc: String) case class Project( id: Option[BSONObjectID], description: LocaleText) object Project { implicit object LocaleTextBSONReader extends BSONReader[LocaleText] { def fromBSON(document: BSONDocument): LocaleText = { val doc = document.toTraversable LocaleText( doc.getAs[BSONString]("enDesc").map(_.value).get, doc.getAs[BSONString]("frDesc").map(_.value).get ) } } implicit object LocaleTextBSONWriter extends BSONWriter[LocaleText] { def toBSON(localText: LocaleText) = { BSONDocument( "enDesc" -> BSONString(localText.enDesc), "frDesc" -> BSONString(localText.frDesc) ) } } implicit object ProjectReader extends BSONReader[Project]{ def fromBSON(doc: BSONDocument): Project = { val document = doc.toTraversable Project( document.getAs[BSONObjectID]("_id"), document.getAs[BSONString]("iatiId").map(_.value).get, LocaleTextBSONReader.fromBSON(document.getAs[BSONDocument]("description").get) } } implicit object ProjectWriter extends BSONWriter[Project]{ def toBSON(project: Project): BSONDocument = { BSONDocument( "_id" -> project.id.getOrElse(BSONObjectID.generate), "iatiId" -> BSONString(project.iatiId), "description" -> LocaleTextBSONWriter.toBSON(project.description) } } |
如何将 Project.description 转换为 Mongo 的 LocaleText 列表到 BSONArray?如果您能对我的问题有所了解,我将不胜感激。非常感谢您的帮助。
最后我找到了我自己的问题的解决方案,希望这能帮助其他一些在 ReactiveMongo 0.8 上苦苦挣扎的人:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | case class LocaleText( lang: String, desc: String) case class Project( id: Option[BSONObjectID], descriptions: List[LocaleText]) object Project { implicit object LocaleTextBSONReader extends BSONReader[LocaleText] { def fromBSON(document: BSONDocument): LocaleText = { val doc = document.toTraversable LocaleText( doc.getAs[BSONString]("lang").get.value, doc.getAs[BSONString]("desc").get.value ) } } implicit object LocaleTextBSONWriter extends BSONWriter[LocaleText] { def toBSON(localText: LocaleText) = { BSONDocument( "lang" -> BSONString(localText.lang), "desc" -> BSONString(localText.desc) ) } } implicit object ProjectReader extends BSONReader[Project]{ def fromBSON(doc: BSONDocument): Project = { val document = doc.toTraversable Project( document.getAs[BSONObjectID]("_id"), document.getAs[BSONArray]("descriptions").get.toTraversable.toList.map { descText => LocaleTextBSONReader.fromBSON(descText.asInstanceOf[TraversableBSONDocument] } ) } } implicit object ProjectWriter extends BSONWriter[Project]{ def toBSON(project: Project): BSONDocument = { BSONDocument( "_id" -> project.id.getOrElse(BSONObjectID.generate), "descriptions" -> BSONArray(project.descriptions.map { description => LocaleTextBSONWriter.toBSON(description) }: _*) } } |
这可能是图书馆的问题。我使用最新版本的 reactivemongo 测试了你的代码,它编译得很好(我需要调整你的代码以适应 BSONReaders 和 BSONWriters 的新语法,但这不应该对错误产生任何影响)。
使用 reactivemongo 0.10.0 你甚至可以使用新提供的宏:
1 2 3 4 5 6 7 8 9 10 11 12 13 | import reactivemongo.bson._ case class LocaleText(id: Option[BSONObjectID], lang: String, textDesc: String) object LocaleText { implicit val localeTextBSONHandler = Macros.handler[LocaleText] } case class Project(id: Option[BSONObjectID], description: List[LocaleText], budget: Option[Long]) object Project { implicit val projectBSONHandler = Macros.handler[Project] } |