0

I'm using play2.5 + play2-reactivemongo 0.12.3.

BSONObjectID is inserted as array and BSONDateTime is inserted as NumberLong on mongoDB with following codes.

> db.Example.find()
{ "_id" : { "array" : [ 89, 55, -10, 60, -40, 0, 0, -61, 0, -94, 126, 23 ] }, "created" : NumberLong("1496839744818") }

I want BSONObjectID to be inserted as ObjectID and DateTime to be inserted as BSONDateTime. Is it possible to do with type parameter?

TemporalModel.scala

trait TemporalModel {
  var _id: Option[BSONObjectID]
  var created: Option[DateTime]
}

Example.scala

import org.joda.time.DateTime
...

case class Example(
  var_id: Option[BSONObjectID],
  str: String,
  var created: Option[DateTime]
) extends TemporalModel

object Example {

  //implicit val objectIdRead: Reads[BSONObjectID] =
  val objectIdRead: Reads[BSONObjectID] =
    (__ \ "$oid").read[String].map { oid =>
      BSONObjectID(oid)
  }

  //implicit val objectIdWrite: Writes[BSONObjectID] = new Writes[BSONObjectID] {
  val objectIdWrite: Writes[BSONObjectID] = new Writes[BSONObjectID] {
    def writes(objectId: BSONObjectID): JsValue = Json.obj(
      "$oid" -> objectId.stringify
    )
  }

  //implicit val dateTimeRead: Reads[DateTime] =
  val dateTimeRead: Reads[DateTime] =
    (__ \ "$date").read[Long].map { dateTime =>
      new DateTime(dateTime)
  }

  //implicit val dateTimeWrite: Writes[DateTime] = new Writes[DateTime] {
  val dateTimeWrite: Writes[DateTime] = new Writes[DateTime] {
    def writes(dateTime: DateTime): JsValue = Json.obj(
      "$date" -> dateTime.getMillis
    )
  }

  implicit val objectIdFormats = Format(objectIdRead, objectIdWrite)
  implicit val dateTimeFormats = Format(dateTimeRead, dateTimeWrite)
  //implicit val bsonObjectIDJsonFormat = Json.format[BSONObjectID]
  implicit val exampleJsonFormat = Json.format[Example]

  def apply(str: String): Example = {
    new Example(null, str, null)
  }
}

BaseDAO.scala

class BaseDAO[T] {
  ...
  val collectionName: String
  lazy val collection: Future[JSONCollection] = reactiveMongoApi.database.map(_.collection(collectionName))

  def toJs[K : Writes](o: K) = Json.toJson(o).as[JsObject]

  def insert(document: T)(implicit writer: Writes[T]): Future[T] = {
    document._id = Some(BSONObjectID.generate)
    document.created = Some(DateTime.now)
    collection.flatMap(_.insert(toJs(document)))
  }
}

ExampleDAO.scala

trait ExampleDAO extends BaseDAO[Example]

class ExampleDAOImpl @Inject() (val reactiveMongoApi: ReactiveMongoApi) 
extends ExampleDAO {
  val collectionName = "example"
}
Uske
  • 173
  • 1
  • 1
  • 8
  • Nothing to do with your question, but you could replace `Writes` with `OWrites` for `toJs` parameter `K`, to make sure that `as[JsObject]` won't throw an exception. – Cyrille Corpet Jun 07 '17 at 14:06
  • Could you explain what doesn't suit you in the code you wrote? – Cyrille Corpet Jun 07 '17 at 14:10
  • Thank you for your advice. I want "_id" in Example case class to be inserted as ObjectID but it's inserted as array and also want "created" to be inserted as ISODate. it's inserted as NumberLong with my code. – Uske Jun 07 '17 at 14:37
  • Does `objectId.stringify` really convert the `BSONObjectID` to a hash string? Not tested but i think `BSONObjectID` as a binary data is converted to array from my analysis. Why dont you use `toString` – Orar Jun 07 '17 at 15:25
  • I also tried toString. But, the result was the same as stringify. – Uske Jun 07 '17 at 16:37

1 Answers1

0

The problem is that you have too many implicits in-scope. However, since those are resolved in the macro, there seems to be no compilation error.

What you should do:

  • remove all implicit modifiers in front of your Reads and Writes for BSONObjectID and DateTime
  • remove bsonObjectIDJsonFormat (which has same type as objectIdFormats, so this should not be allowed by the compiler)
Cyrille Corpet
  • 5,265
  • 1
  • 14
  • 31
  • I commented out all implicit. But nothing change. – Uske Jun 07 '17 at 16:00
  • Do not remove everything, just the word `implicit` in front of your 4 `Reads` and `Writes` (keep the `Format`s) and `bsonObjectIDJsonFormat` – Cyrille Corpet Jun 07 '17 at 16:04
  • Thanks. I misunderstood. And it's not resolved with modified code. Am I missing something? – Uske Jun 07 '17 at 16:32
  • I modified some codes and been trying to resolve the problem. But, still doesn't work as I expect. – Uske Jun 08 '17 at 09:05
  • that's weird, I have the same code as you have in `Example.scala`, and It serializes `Example`s as you want them. Have you tried to pass the `Writes[Example]` explicitly to your `insert` method, to see if it really uses`exampleJsonFormat`? – Cyrille Corpet Jun 08 '17 at 09:08
  • I added println() into objectIdWrite and I confirmed it doesn't output via test code. really weird. – Uske Jun 08 '17 at 10:38
  • You should check, say with IntelliJ, what implicit is used when you call insert. – Cyrille Corpet Jun 08 '17 at 11:43
  • sorry. I just confirmed println is called once with specs2 test. IntelliJ? my development environment is on ubuntu and vim.. – Uske Jun 08 '17 at 11:53
  • It's properly inserted as I expected!! The problem was that I was confirming via embedded mongodb flapdoodle with specs2. I don't know why test code let the data insert real mongodb.. Anyway, really thanks a lot. I realized it after I installed IntelliJ on mac. It's quite useful. – Uske Jun 09 '17 at 01:26