sql >> Base de Datos >  >> NoSQL >> MongoDB

Asignación de documentos MongoDB a la clase de caso con tipos pero sin documentos incrustados

Si es posible. En realidad, es incluso más simple que tener un subdocumento de "usuario" en un "tweet". Cuando "usuario" es una referencia, es solo un valor escalar, MongoDB y "Subconjunto" no tienen mecanismos para consultar campos de subdocumentos.

He preparado un fragmento de código REPLable simple para usted (se supone que tiene dos colecciones:"tweets" y "users").

Preparativos...

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"

Nuestro User clase de caso

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

Una serie de campos para tweets y usuario

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

Aquí empiezan a pasar cosas más complicadas. Lo que necesitamos es un ValueReader que es capaz de obtener ObjectId basado en el nombre del campo, pero luego va a otra colección y lee un objeto desde allí.

Esto se puede escribir como una sola pieza de código, que hace todo a la vez (es posible que vea una variante de este tipo en el historial de respuestas), pero sería más idiomático expresarlo como una combinación de lectores. Supongamos que tenemos un ValueReader[User] que lee desde DBObject :

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

Lo que queda es un ValueReader[T] genérico que espera ObjectId y recupera un objeto de una colección específica utilizando el lector subyacente proporcionado:

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

Entonces, podemos decir nuestra clase de tipo para leer User s de las referencias es simplemente

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

Y así es como lo usarías:

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