Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add updateOnlyNotNullProperties boolean in order to update only not null properties #115

Closed
qcho opened this issue Feb 19, 2019 · 2 comments · Fixed by qcho/kmongo#1
Closed

Comments

@qcho
Copy link

qcho commented Feb 19, 2019

Hi,

I'm trying to perform partial updates based on the fact that ObjectMappingConfiguration.serializeNull = false. So i expect that my Document doesn't serialize field2 when it's value is null. Obviously i'm using kmongo-native

Am I interpreting this feature wrong?? btw no documentation on this topic anywhere. Here is a Unit test I've made explaining the behavior

import org.bson.codecs.pojo.annotations.BsonId
import org.litote.kmongo.* //NEEDED! import KMongo extensions
import org.litote.kmongo.util.ObjectMappingConfiguration
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

class AppTest {

    data class Document(
            @BsonId val id: Int,
            val field1: String,
            val field2: String?
    )

    @Test fun testMongoBehaviours() {
        val client = KMongo.createClient()
        val database = client.getDatabase("test")
        val col = database.getCollection<Document>()
        ObjectMappingConfiguration.serializeNull = false // This is the default value anyway. Just being verbose
        run {
            col.save(Document(1, "Hello1", null))
            val document = col.findOne(Document::id eq 1)
            assertNotNull(document)
            assertEquals("Hello1", document.field1)
            assertEquals(null, document.field2)
        }
        run {
            col.save(Document(1, "Hello2", "World2"))
            val document = col.findOne(Document::id eq 1)
            assertNotNull(document)
            assertEquals("Hello2", document.field1)
            assertEquals("World2", document.field2)
        }
        run {
            col.save(Document(1, "Hello3", null))
            val document = col.findOne(Document::id eq 1)
            assertNotNull(document)
            assertEquals("Hello3", document.field1)
            assertEquals("World2", document.field2) // EXCEPTION THIS LINE. I expect this null value not to be serialized
        }
    }
}

I get

java.lang.AssertionError: 
Expected :World2
Actual   :null
@qcho
Copy link
Author

qcho commented Feb 19, 2019

On further investigation I think the issue is at PojoClassMappingTypeService.kt

    override fun filterIdToBson(obj: Any): BsonDocument {
        val bsonDocument = BsonDocument()
        val bsonWriter = BsonDocumentWriter(bsonDocument)
        codecRegistryWithNullSerialization // <--- HERE is always using WithNullSerialization
            .get(obj.javaClass)
            ?.encode(
                bsonWriter,
                obj,
                EncoderContext.builder().build()
            )
        bsonDocument.remove("_id")
        return bsonDocument
    }

Maybe it needs to call ClassMappingType.codecRegistry(MongoClientSettings.getDefaultCodecRegistry()) the same as ::toExtendedJson does?

qcho added a commit to qcho/kmongo that referenced this issue Feb 19, 2019
codecRegistryWithNullSerialization was hardCoded. Trying to fix Litote#115
@qcho
Copy link
Author

qcho commented Feb 19, 2019

I've fixed the issue. Sending a pull request now.

It was a mix of two things.
First: PojoClassMappingTypeService was wrong due to #115 (comment)
Second: I was wrong on the usage of save() because it doesn't perform updateOne, instead it does a replaceOne following mongodb's spec.

So doing col.updateOne(<DOCUMENT>, upsert()) instead of col.save(<DOCUMENT>) plus First fixed does the trick.

import org.bson.codecs.pojo.annotations.BsonId
import org.litote.kmongo.* //NEEDED! import KMongo extensions
import org.litote.kmongo.util.ObjectMappingConfiguration
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

class AppTest {

    data class Document(
            @BsonId val id: Int,
            val field1: String,
            val field2: String?
    )

    @Test fun testMongoBehaviours() {
        val client = KMongo.createClient() //use coroutine extension
        val database = client.getDatabase("test") //normal java driver usage
        val col = database.getCollection<Document>() //KMongo extension method
        ObjectMappingConfiguration.serializeNull = false
        col.deleteMany()
        run {
            col.updateOne(Document(1, "Hello1", null), upsert())
            val document = col.findOne(Document::id eq 1)
            assertNotNull(document)
            assertEquals("Hello1", document.field1)
            assertEquals(null, document.field2)
        }
        run {
            col.updateOne(Document(1, "Hello2", "World2"), upsert())
            val document = col.findOne(Document::id eq 1)
            assertNotNull(document)
            assertEquals("Hello2", document.field1)
            assertEquals("World2", document.field2)
        }
        run {
            col.updateOne(Document(1, "Hello3", null), upsert())
            val document = col.findOne(Document::id eq 1)
            assertNotNull(document)
            assertEquals("Hello3", document.field1)
            assertEquals("World2", document.field2) // because it was null
        }
    }
}

@qcho qcho reopened this Feb 19, 2019
@zigzago zigzago changed the title Problem with kmongo-native and serializeNull = false Add updateOnlyNotNullProperties boolean in order to update only not null properties Feb 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants