-->

MongoDB的映射文件的情况下类类型,但不嵌入文档(Mapping MongoDB documen

2019-07-31 00:45发布

子集看起来像一个有趣的,瘦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在数据库中。 这是可能的,如果是这样,怎么样? 有什么好的办法?

Answer 1:

是的,这是可能的。 其实它比有一个“用户”在“推特”子文档更简单。 当“用户”是一个参考,它仅仅是一个标量值,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))
}


Answer 2:

亚历山大·阿扎罗夫答案可能工作正常,但我个人不会做这种方式。

你有什么是鸣叫,只有拥有的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