子集看起来像一个有趣的,瘦MongoDB的包装。
在给出的实施例之一,有文和用户。 然而, User
是子文档 Tweet
。 在传统的SQL,这将被归为两个独立的表与Tweet到用户的外键。 在MongoDB中,这将既不需要DBRef
,存储用户ObjectId
就足够了。
无论是在子集和萨拉特这将导致这些案例类:
case class Tweet(_id: ObjectId, content: String, userId: ObjectId)
case class User(_id: ObjectId, name: String)
因此,有没有保证,在的ObjectId实际上分享Tweet解析为一个用户(使其不太类型安全)。 我也有写的每个引用User类相同的查询(或移动到某些性状)。
所以我想实现是有case class Tweet(_id: ObjectId, content: String, userId: User)
,在代码中,和ObjectId
在数据库中。 这是可能的,如果是这样,怎么样? 有什么好的办法?
是的,这是可能的。 其实它比有一个“用户”在“推特”子文档更简单。 当“用户”是一个参考,它仅仅是一个标量值,MongoDB的和“子集”有没有机制来查询子文档领域。
我已经准备了一个简单的代码片断REPLable为你(它假定你有两个集合 - “推特”和“用户”)。
准备...
import org.bson.types.ObjectId
import com.mongodb._
import com.osinka.subset._
import Document.DocumentId
val db = new Mongo("localhost") getDB "test"
val tweets = db getCollection "tweets"
val users = db getCollection "users"
我们的User
案例类
case class User(_id: ObjectId, name: String)
一种用于鸣叫和用户数量的字段
val content = "content".fieldOf[String]
val user = "user".fieldOf[User]
val name = "name".fieldOf[String]
这里更复杂的事情开始发生。 我们需要的是一个ValueReader
这是能够得到的ObjectId
基于字段名,但是随后到另一个集合,并从那里读的对象。
这可以写成一个单件的代码,做所有的事情一次(你可以看到在回答这样一个历史的变体),但它会更地道地表达出来作为读者的组合。 假设我们有一个ValueReader[User]
从读取DBObject
:
val userFromDBObject = ValueReader({
case DocumentId(id) ~ name(name) => User(id, name)
})
剩下的就是一个普通的ValueReader[T]
一个期望ObjectId
和使用提供潜在读者的特定集合检索的对象:
class RefReader[T](val collection: DBCollection, val underlying: ValueReader[T]) extends ValueReader[T] {
override def unpack(o: Any):Option[T] =
o match {
case id: ObjectId =>
Option(collection findOne id) flatMap {underlying.unpack _}
case _ =>
None
}
}
然后,我们可以说我们的阅读类型类User
从引用s是仅仅
implicit val userReader = new RefReader[User](users, userFromDBObject)
(我很感谢你为这个问题,因为这种使用情况是相当罕见的,我并没有真正的动机,建立一个通用的解决方案。我想我需要包括这种帮手到“子集”终于......我要感谢你这种方法的反馈)
这是你将如何使用它:
import collection.JavaConverters._
tweets.find.iterator.asScala foreach {
case Document.DocumentId(id) ~ content(content) ~ user(u) =>
println("%s - %s by %s".format(id, content, u))
}
亚历山大·阿扎罗夫答案可能工作正常,但我个人不会做这种方式。
你有什么是鸣叫,只有拥有的ObjectId参考用户。 而要加载鸣叫负载时的用户,因为您的域名它可能是更容易操控。 在任何情况下,除非你使用子文档(并不总是一个不错的选择),则必须再次查询数据库检索用户数据,而这正是由亚历山大·阿扎罗夫完成。
你宁愿做一个转换功能,其转换一个Tweet到一个TweetWithUser或类似的东西。
def transform(tweet: Tweet) = TweetWithUser( tweet.id, tweet.content, findUserWithId(tweet.userId) )
我真的不明白你为什么会想到一个框架来解决的东西,你可以在一个单一的代码行很容易做自己。
请记住在你的应用程序,在某些情况下,你甚至不需要整个用户对象,所以它是昂贵的查询数据库的两倍,而它并不总是必要的。 您应该只使用带有完整的用户数据的情况下上课的时候,你真正需要的是用户数据,因为它似乎更方便的不只是总是加载完整的用户数据。
或者,如果你仍要操纵用户对象,你将有一个用户代理上,您可以访问ID直接属性,以及任何其他连接,数据库查询将被完成。 在Java / SQL,休眠与关系的延迟加载这样做,但我不知道它的使用与MongoDB的一个好主意,它打破不变性
文章来源: Mapping MongoDB documents to case class with types but without embedded documents