Search code examples
mongodbplayframeworkplayframework-2.3reactivemongoplay-reactivemongo

In Play! application base document fields are not persisted to mongo db (reactive mongo plugin)


I have Play 2.3 appliction with reactive mongo plugin. I have base document:

trait TemporalDocument {
  val created: Option[DateTime] = Some(new DateTime())
  val updated: Option[DateTime] = Some(new DateTime())
}

and one of the concrete document:

case class User(name: String) extends TemporalDocument

object User{
  implicit val userFormat = Json.format[User]
}

So when I persist it to mongo db using reactive mongo plugin only name is persisted, created/updated fields are not.

My repository looks like:

trait MongoDocumentRepository[T <: TemporalDocument] extends ContextHolder {

  private val db = ReactiveMongoPlugin.db

  def insert(document: T)(implicit writer: Writes[T]): Future[String] = {
    collection.insert(document).map {
      lastError => lastError.toString
    } recover {
      case e: Exception => sys.error(e.getMessage)
    }
  }

  private def collection: JSONCollection = db.collection[JSONCollection](collectionName)

  implicit object BSONDateTimeHandler extends BSONHandler[BSONDateTime, DateTime] {
    def read(time: BSONDateTime) = new DateTime(time.value)

    def write(jdtime: DateTime) = BSONDateTime(jdtime.getMillis)
  }
}

Problem is that I will have many documents extended from base document and I do not want each time init those dates and probably some other fields. Is it possible to do something like this?


Solution

  • Firstly, we can halve the surface-area of the problem; Reactive Mongo and/or the Play Reactive Mongo Plugin are not relevant here, it's Play's JSON macros that build the appropriate JSON structures (or not, in this case) that are responsible.

    If I set up a TemporalDocument and User as in your code, and then write it out:

    val user = User("timmy")
    
    println(Json.toJson(user))
    

    I get:

    {"name":"timmy"}
    

    I haven't looked into it, but I suspect this is because the created and updated fields don't appear in the "field list" of the User case class.

    If I rework your code a little bit like this:

    trait TemporalDocument {
      val created: Option[DateTime]
      val updated: Option[DateTime]
    }
    
    case class User(
                    name: String,
                    val created: Option[DateTime] = Some(new DateTime()),
                    val updated: Option[DateTime] = Some(new DateTime())) 
                    extends TemporalDocument
    

    Then the same test code gets the desired behaviour from play-json:

    {"name":"timmy","created":1410930805042,"updated":1410930805071}