MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Mappatura dei documenti MongoDB alla classe case con tipi ma senza documenti incorporati

Si è possibile. In realtà è ancora più semplice che avere un sottodocumento "utente" in un "tweet". Quando "utente" è un riferimento, è solo un valore scalare, MongoDB e "Subset" non hanno meccanismi per interrogare i campi dei documenti secondari.

Ho preparato per te un semplice frammento di codice REPLable (presuppone che tu abbia due raccolte:"tweet" e "users").

Preparativi...

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"

Il nostro User classe di casi

case class User(_id: ObjectId, name: String)

Numerosi campi per tweet e utente

val content = "content".fieldOf[String]
val user = "user".fieldOf[User]
val name = "name".fieldOf[String]

Qui iniziano ad accadere cose più complicate. Quello di cui abbiamo bisogno è un ValueReader che è in grado di ottenere ObjectId in base al nome del campo, ma poi passa a un'altra raccolta e legge un oggetto da lì.

Questo può essere scritto come un unico pezzo di codice, che fa tutte le cose in una volta (potresti vedere una tale variante nella cronologia delle risposte), ma sarebbe più idiomatico esprimerlo come una combinazione di lettori. Supponiamo di avere un ValueReader[User] che legge da DBObject :

val userFromDBObject = ValueReader({
  case DocumentId(id) ~ name(name) => User(id, name)
})

Quello che resta è un generico ValueReader[T] che si aspetta ObjectId e recupera un oggetto da una raccolta specifica utilizzando il lettore sottostante fornito:

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
    }
}

Quindi, possiamo dire la nostra classe di tipo per leggere User s da riferimenti è semplicemente

implicit val userReader = new RefReader[User](users, userFromDBObject)

Ed ecco come lo useresti:

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))
}