0

I'm trying to store java.time.Instant in mongo's native Date format.

Official ReactiveMongo documentation states that date time should be serialized as

JsObject with a $date JsNumber field with the timestamp (milliseconds) as value

enter image description here

(http://reactivemongo.org/releases/1.0/documentation/json/overview.html)

I follow this rule, but the value is not stored as mongo's Date, instead, it is stored as a regular JSON (BSON) document:

{
    "_id" : ObjectId("6057b962af0000af00e81ec7"),
    "username" : "john",
    "createdAt" : {
        "$date" : NumberLong(1616361826198)
    }
}

Scala source code that stores the document:

import play.api.libs.json.Json
import reactivemongo.api.DB
import reactivemongo.api.bson.BSONObjectID
import reactivemongo.api.bson.collection.BSONCollection
import java.time.Instant
import scala.concurrent.{ ExecutionContext, Future }
import reactivemongo.play.json.compat._
import json2bson._
import reactivemongo.api.commands.WriteResult

class Repo(database: DB)(implicit ec: ExecutionContext) {

  def collection: BSONCollection =
    database.collection("users")

  def insertDocument(): Future[WriteResult] = {
    val doc = Json.obj(
      "_id"       -> BSONObjectID.generate(),
      "username"  -> "john",
      "createdAt" -> Json.obj("$date" -> Instant.now().toEpochMilli)
    )

    collection.insert.one(doc)
  }
}

What's wrong here?

P.S.:

If I change extended BSON syntax

Json.obj("$date" -> Instant.now().toEpochMilli)

to BSONDateTime:

  ...
  "createdAt" -> BSONDateTime(Instant.now().toEpochMilli)
  ...

it works.

But still, why doesn't it work with play JSON + extended syntax?

cchantep
  • 9,118
  • 3
  • 30
  • 41
Teimuraz
  • 8,795
  • 5
  • 35
  • 62
  • 1. There are several examples about JSON extended iin mailing list and documentation. 2. The `extended._` seems missing. 3. Using JSON in this case (to represent internal query) is not only useless but more complex and has increased cost due to conversions – cchantep Mar 22 '21 at 08:11

1 Answers1

1

The MongoDB JSON extended representation is not a valid v2 one for date. The $date value must be either a formatted string or a long {"$numberLong": "<millis>"}

scala> BSONValue.pretty(implicitly[BSONValue](Json.obj("$date" -> Instant.now().toEpochMilli)))
res12: String =
{
  '$date': NumberLong(1616415630059)
}

scala> BSONValue.pretty(implicitly[BSONValue](Json.obj("$date" -> Instant.now().toString)))
res13: String = ISODate('2021-03-22T12:20:37.571Z')

scala> BSONValue.pretty(implicitly[BSONValue](Json.obj("$date" -> Json.obj("$numberLong" -> Instant.now().toEpochMilli.toString))))
res16: String = ISODate('2021-03-22T12:21:48.843Z')

That being said, using JSON to represent internal query (without value coming as JSON from elsewhere, e.g. a REST API), is useless, and costful.

When required, make sure to have all the required imports (like the extended._ one).

cchantep
  • 9,118
  • 3
  • 30
  • 41