MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Mappage des documents MongoDB à la classe de cas avec des types mais sans documents intégrés

Oui c'est possible. En fait c'est encore plus simple que d'avoir un sous-document "utilisateur" dans un "tweet". Lorsque "user" est une référence, il s'agit simplement d'une valeur scalaire, MongoDB et "Subset" n'ont aucun mécanisme pour interroger les champs de sous-document.

J'ai préparé un simple extrait de code REPLable pour vous (il suppose que vous avez deux collections -- "tweets" et "utilisateurs").

Préparatifs...

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"

Notre User classe de cas

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

Un certain nombre de champs pour les tweets et l'utilisateur

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

Ici, des choses plus compliquées commencent à se produire. Ce dont nous avons besoin est un ValueReader qui est capable d'obtenir ObjectId basé sur le nom du champ, mais passe ensuite à une autre collection et lit un objet à partir de là.

Cela peut être écrit comme un seul morceau de code, qui fait tout à la fois (vous pouvez voir une telle variante dans l'historique des réponses), mais il serait plus idiomatique de l'exprimer comme une combinaison de lecteurs. Supposons que nous ayons un ValueReader[User] qui lit à partir de DBObject :

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

Ce qui reste est un ValueReader[T] générique qui attend ObjectId et récupère un objet d'une collection spécifique à l'aide du lecteur sous-jacent fourni :

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

Ensuite, nous pouvons dire notre classe de type pour lire User s à partir de références est simplement

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

Et voici comment vous l'utiliseriez :

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