Uploaded image for project: 'Java Driver'
  1. Java Driver
  2. JAVA-5784

Kotlinx datetime serializer is not applied to updateOne operation

    • Type: Icon: Bug Bug
    • Resolution: Unresolved
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: None
    • Component/s: Kotlin
    • None
    • Java Drivers
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?
    • None
    • None
    • None
    • None
    • None
    • None

      Summary

      When updating a kotlinx.datetime.Instant field with the non-default InstantAsBsonDateTime serializer using MongoCollection.updateOne, the serializer is seemingly not applied and the instant gets serialized as a string. This leads to 
      BsonInvalidOperationException on any subsequent find operations as deserialization fails.

      Please provide the version of the driver. If applicable, please provide the MongoDB server version and topology (standalone, replica set, or sharded cluster).

      This happens with the latest driver and kotlinx versions

      org.mongodb:mongodb-driver-kotlin-coroutine:5.3.1
      org.mongodb:mongodb-driver-kotlin-extensions:5.3.1
      org.mongodb:bson-kotlinx:5.3.1
      org.jetbrains.kotlinx:kotlinx-serialization-core:1.8.0
      org.jetbrains.kotlinx:kotlinx-datetime:0.6.1

      How to Reproduce

      The behaviour is reproducible with the following test

      import com.mongodb.ConnectionString
      import com.mongodb.MongoClientSettings
      import com.mongodb.kotlin.client.coroutine.MongoClient
      import com.mongodb.kotlin.client.model.Filters.eq
      import com.mongodb.kotlin.client.model.Updates.set
      import io.kotest.assertions.throwables.shouldNotThrow
      import io.kotest.core.spec.style.FunSpec
      import io.kotest.matchers.nulls.shouldNotBeNull
      import kotlinx.coroutines.flow.firstOrNull
      import kotlinx.datetime.Clock
      import kotlinx.datetime.Instant
      import kotlinx.serialization.Contextual
      import kotlinx.serialization.SerialName
      import kotlinx.serialization.Serializable
      import org.bson.BsonInvalidOperationException
      import org.bson.types.ObjectId
      import org.testcontainers.containers.MongoDBContainer
      import org.testcontainers.utility.DockerImageName
      
      class MongoTest : FunSpec({
      
          @Serializable
          data class Foo(
              @SerialName("_id")
              @Contextual
              val id: ObjectId = ObjectId(),
              @Contextual // Or @Serializable(with = InstantAsBsonDateTime::class)
              val instant: Instant,
          )
      
          val mongoDbContainer = MongoDBContainer(DockerImageName.parse("mongo:8.0")).also { it.start() }
          val client = MongoClient.create(
              MongoClientSettings.builder()
                  .applyConnectionString(ConnectionString(mongoDbContainer.connectionString))
                  .build()
          )
          val database = client.getDatabase("foo")
          val collection = database.getCollection<Foo>("foos")
      
          test("should serialize and deserialize Foo correctly after updating instant") {
              val foo = Foo(instant = Clock.System.now())
              collection.insertOne(foo)
      
              // updating instant field with replaceOne works as expected
              val newInstant = Clock.System.now()
              collection.replaceOne(Foo::id eq foo.id, foo.copy(instant = newInstant))
              var found = collection.find(Foo::id eq foo.id).firstOrNull()
              found.shouldNotBeNull()
      
              // deserializing instant field after updating it with updateOne throws BsonInvalidOperationException
              // Caused by: org.bson.BsonInvalidOperationException: Value expected to be of type DATE_TIME is of unexpected type STRING
              val anotherNewInstant = Clock.System.now()
              collection.updateOne(Foo::id eq foo.id, Foo::instant set anotherNewInstant)
              shouldNotThrow<BsonInvalidOperationException> {
                  collection.find(Foo::id eq foo.id).firstOrNull()
              }
          }
      })
        

      Steps to reproduce. If possible, please include a Short, Self Contained, Correct (Compilable), Example.

      Additional Background

      Please provide any additional background information that may be helpful in diagnosing the bug.

            Assignee:
            slav.babanin@mongodb.com Slav Babanin
            Reporter:
            riku.heinonen@wolt.com Riku Heinonen
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              None
              None
              None
              None