Ja es ist möglich. Tatsächlich ist es sogar einfacher, als ein "Benutzer"-Unterdokument in einem "Tweet" zu haben. Wenn „user“ eine Referenz ist, ist es nur ein Skalarwert, MongoDB und „Subset“ haben keine Mechanismen zum Abfragen von Unterdokumentfeldern.
Ich habe ein einfaches REPLable-Code-Snippet für Sie vorbereitet (es wird davon ausgegangen, dass Sie zwei Sammlungen haben – „tweets“ und „users“).
Vorbereitungen...
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"
Unser User
Fallklasse
case class User(_id: ObjectId, name: String)
Eine Reihe von Feldern für Tweets und Benutzer
val content = "content".fieldOf[String]
val user = "user".fieldOf[User]
val name = "name".fieldOf[String]
Hier beginnen kompliziertere Dinge zu passieren. Was wir brauchen, ist ein ValueReader
die in der Lage ist, ObjectId
zu erhalten basierend auf dem Feldnamen, geht dann aber zu einer anderen Sammlung und liest ein Objekt von dort.
Dies kann als ein einzelnes Stück Code geschrieben werden, das alle Dinge auf einmal tut (Sie können eine solche Variante im Antwortverlauf sehen), aber es wäre idiomatischer, es als eine Kombination von Lesern auszudrücken. Angenommen, wir haben einen ValueReader[User]
die von DBObject
liest :
val userFromDBObject = ValueReader({
case DocumentId(id) ~ name(name) => User(id, name)
})
Übrig bleibt ein generischer ValueReader[T]
das erwartet ObjectId
und ruft ein Objekt aus einer bestimmten Sammlung unter Verwendung des bereitgestellten zugrunde liegenden Lesers ab:
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
}
}
Dann können wir unsere Typklasse zum Lesen von User
sagen s aus Referenzen ist lediglich
implicit val userReader = new RefReader[User](users, userFromDBObject)
Und so würden Sie es verwenden:
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))
}