Ja, det er muligt. Faktisk er det endnu nemmere end at have et "bruger" underdokument i et "tweet". Når "bruger" er en reference, er det kun en skalær værdi, MongoDB og "Subset" har ingen mekanismer til at forespørge underdokumentfelter.
Jeg har forberedt et simpelt REPLable kodestykke til dig (det antager, at du har to samlinger -- "tweets" og "brugere").
Forberedelser...
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"
Vores User
sagsklasse
case class User(_id: ObjectId, name: String)
En række felter til tweets og bruger
val content = "content".fieldOf[String]
val user = "user".fieldOf[User]
val name = "name".fieldOf[String]
Her begynder der at ske mere komplicerede ting. Det, vi har brug for, er en ValueReader
der er i stand til at få ObjectId
baseret på feltnavn, men går så til en anden samling og læser et objekt derfra.
Dette kan skrives som et enkelt stykke kode, der gør alle ting på én gang (du kan se en sådan variant i svarhistorikken), men det ville være mere idiomatisk at udtrykke det som en kombination af læsere. Antag, at vi har en ValueReader[User]
der læser fra DBObject
:
val userFromDBObject = ValueReader({
case DocumentId(id) ~ name(name) => User(id, name)
})
Hvad der er tilbage er en generisk ValueReader[T]
der forventer ObjectId
og henter et objekt fra en specifik samling ved hjælp af den medfølgende underliggende læser:
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
}
}
Derefter kan vi sige vores typeklasse til at læse User
s fra referencer er blot
implicit val userReader = new RefReader[User](users, userFromDBObject)
Og sådan ville du bruge det:
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))
}