Vâng nó có thể. Trên thực tế, nó thậm chí còn đơn giản hơn việc có một tài liệu phụ "người dùng" trong một "tweet". Khi "người dùng" là một tham chiếu, nó chỉ là một giá trị vô hướng, MongoDB và "Tập hợp con" không có cơ chế để truy vấn các trường tài liệu con.
Tôi đã chuẩn bị một đoạn mã REPLable đơn giản cho bạn (giả sử bạn có hai bộ sưu tập - "tweet" và "người dùng").
Chuẩn bị ...
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"
User
của chúng tôi trường hợp lớp
case class User(_id: ObjectId, name: String)
Một số trường dành cho tweet và người dùng
val content = "content".fieldOf[String]
val user = "user".fieldOf[User]
val name = "name".fieldOf[String]
Ở đây những điều phức tạp hơn bắt đầu xảy ra. Những gì chúng ta cần là một ValueReader
có khả năng nhận được ObjectId
dựa trên tên trường, nhưng sau đó chuyển đến một bộ sưu tập khác và đọc một đối tượng từ đó.
Điều này có thể được viết dưới dạng một đoạn mã duy nhất, thực hiện tất cả mọi thứ cùng một lúc (bạn có thể thấy một biến thể như vậy trong lịch sử câu trả lời), nhưng sẽ khó hiểu hơn nếu diễn đạt nó như một sự kết hợp của các trình đọc. Giả sử chúng ta có ValueReader[User]
đọc từ DBObject
:
val userFromDBObject = ValueReader({
case DocumentId(id) ~ name(name) => User(id, name)
})
Những gì còn lại là một ValueReader[T]
chung chung điều đó mong đợi ObjectId
và truy xuất một đối tượng từ một bộ sưu tập cụ thể bằng cách sử dụng trình đọc bên dưới được cung cấp:
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
}
}
Sau đó, chúng ta có thể nói lớp loại của chúng ta để đọc User
s từ các tài liệu tham khảo chỉ là
implicit val userReader = new RefReader[User](users, userFromDBObject)
Và đây là cách bạn sẽ sử dụng nó:
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))
}