MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Zuordnen von MongoDB-Dokumenten zu Fallklassen mit Typen, aber ohne eingebettete Dokumente

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