MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Mapeando documentos do MongoDB para classe de caso com tipos, mas sem documentos incorporados


Sim é possivel. Na verdade, é ainda mais simples do que ter um subdocumento "usuário" em um "tweet". Quando "usuário" é uma referência, é apenas um valor escalar, MongoDB e "Subconjunto" não possuem mecanismos para consultar campos de subdocumentos.

Eu preparei um trecho de código REPLable simples para você (ele pressupõe que você tenha duas coleções -- "tweets" e "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"

Nosso User classe de caso
case class User(_id: ObjectId, name: String)

Vários campos para tweets e usuários
val content = "content".fieldOf[String]
val user = "user".fieldOf[User]
val name = "name".fieldOf[String]

Aqui começam a acontecer coisas mais complicadas. O que precisamos é de um ValueReader que é capaz de obter ObjectId com base no nome do campo, mas depois vai para outra coleção e lê um objeto de lá.

Isso pode ser escrito como um único pedaço de código, que faz todas as coisas ao mesmo tempo (você pode ver essa variante no histórico de respostas), mas seria mais idiomático expressá-lo como uma combinação de leitores. Suponha que temos um ValueReader[User] que lê de DBObject :
val userFromDBObject = ValueReader({
  case DocumentId(id) ~ name(name) => User(id, name)
})

O que resta é um ValueReader[T] genérico que espera ObjectId e recupera um objeto de uma coleção específica usando o leitor subjacente fornecido:
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
    }
}

Então, podemos dizer que nossa classe de tipo para ler User s de referências é meramente
implicit val userReader = new RefReader[User](users, userFromDBObject)

E é assim que você usaria:
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))
}