scalaplayframeworkreactivemongoplay-reactivemongo

How to create a custom writer/reader that queries the DB?


I have a class Song and a class Album. My goal is to have two separate collections and keep a reference via BSONObjectID in the database, but in my code I want to map these IDs to their respective entities.

Here's my current model:

case class Song(var _id: Option[BSONObjectID] = None,
                   name: String,
                   albumId: BSONObjectID,
                   var created: Option[DateTime] = None,
                   var updated: Option[DateTime] = None
                  )

And I want to replace albumId with album:

Here's my current model:

case class Song(var _id: Option[BSONObjectID] = None,
                   name: String,
                   album: Album,
                   var created: Option[DateTime] = None,
                   var updated: Option[DateTime] = None
                  )

As the documentation states, I have to create a custom writer/reader for that. However, my problem is that I do not know how I can query inside my custom reader/writer the database in order to retrieve all necessary fields to instantiate my Album instance. Is this how it's supposed to work? Querying the database in a model?

object Song {

  import play.api.libs.json._
  import play.api.libs.json.Reads._
  import play.api.libs.functional.syntax._
  import reactivemongo.play.json.BSONFormats.BSONObjectIDFormat

  implicit val songReads: Reads[Song] = (
    (__ \ "_id").readNullable[BSONObjectID].map(_.getOrElse(BSONObjectID.generate)).map(Some(_)) and
      (__ \ "album").read[BSONObjectID].map(x => Album(_id = Option(_), ...)) and // ???
      (__ \ "created").readNullable[DateTime].map(_.getOrElse(new DateTime())).map(Some(_)) and
      (__ \ "updated").readNullable[DateTime].map(_.getOrElse(new DateTime())).map(Some(_))
    ) (Song.apply _)

  implicit val songWrites: OWrites[Song] = (
    (__ \ "_id").writeNullable[BSONObjectID] and
      (__ \ "album").write[BSONObjectID] and // ???
      (__ \ "created").writeNullable[DateTime] and
      (__ \ "updated").writeNullable[DateTime]
    ) (unlift(Song.unapply))

}

Solution

  • As someone discussed, BSONHandlers in ReactiveMongo are responsible for serializing / deserializing a single object, not to manage object relationship.

    Managing relationship hasn't got much to do with serialization and you need to write a separate layer to handle that.

    def loadSong(songName:String) : Future[Song] = // query coll 1 then query coll2 then build Song object