Ja det är möjligt. Egentligen är det ännu enklare än att ha ett "användar"-underdokument i en "tweet". När "användare" är en referens är det bara ett skalärt värde, MongoDB och "Subset" har inga mekanismer för att fråga underdokumentfält.
Jag har förberett ett enkelt REPLable kodavsnitt åt dig (det förutsätter att du har två samlingar -- "tweets" och "users").
Förberedelser...
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"
Vår User
fallklass
case class User(_id: ObjectId, name: String)
Ett antal fält för tweets och användare
val content = "content".fieldOf[String]
val user = "user".fieldOf[User]
val name = "name".fieldOf[String]
Här börjar mer komplicerade saker hända. Vad vi behöver är en ValueReader
som kan få ObjectId
baserat på fältnamn, men går sedan till en annan samling och läser ett objekt därifrån.
Detta kan skrivas som ett enda stycke kod, som gör allt på en gång (du kan se en sådan variant i svarshistoriken), men det skulle vara mer idiomatiskt att uttrycka det som en kombination av läsare. Anta att vi har en ValueReader[User]
som läser från DBObject
:
val userFromDBObject = ValueReader({
case DocumentId(id) ~ name(name) => User(id, name)
})
Det som finns kvar är en generisk ValueReader[T]
som förväntar sig ObjectId
och hämtar ett objekt från en specifik samling med hjälp av den medföljande underliggande läsaren:
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
}
}
Sedan kan vi säga vår typklass för att läsa User
s från referenser är bara
implicit val userReader = new RefReader[User](users, userFromDBObject)
Och så här skulle du använda 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))
}