Litote / kmongo

[deprecated] KMongo - a Kotlin toolkit for Mongo
https://litote.org/kmongo/
Apache License 2.0
781 stars 75 forks source link

ERROR: "toId" function does not creates "$oid" object #402

Closed urosjarc closed 1 year ago

urosjarc commented 1 year ago

If I search db with this command:

this.col<Product>().findOneById(id = id)

I get the entiti that I wanted and this is in the logs...

{"find": "product", "filter": {"_id": {"$oid": "64072d7118cb3276cdee3f64"}} ...

But if I execute this command:

this.col<Product>().findOneById(id = id.toString().toId<Product>())

The entity is not found and this is the logs...

{"find": "product", "filter": {"_id": "64072d7118cb3276cdee3f64"}} ...
mervyn-mccreight commented 1 year ago

I think this is, because there is a difference between a StringId (https://github.com/Litote/kmongo/blob/master/kmongo-id/src/main/kotlin/org/litote/kmongo/id/StringId.kt) and another instance of Id.

I'm not sure, but it might be intended, because in theory you could have basically everything as an Id, as long as it is unique. It's not restricted to be an ObjectId.

But I'm only guessing here.

tfonrouge commented 1 year ago

Hi,

I think that the name of method toId<t>() is a little misleading because it returns a StringId which is not transformed in a {"$oid": "..."} in the bson encoding phase, it just get transformed into his string representation.

The following code shows the ambiguity:

product = col.findOneById(id = ObjectId("64072d7118cb3276cdee3f64")) // Product contains a valid mongo document
id = product?._id  // a WrappedObjectId type (inherited from Id<T>)

val id2 = id.toString().toId<Product>()  // a StringId type (inherited from Id<T>)
product = col.findOneById(id2) // product will be null because the wrong resulting encoding {"_id": "..."}
println(product)

val filter = Product::_id eq id2  // no complains from the compiler!!! (because it simply expects an Id<T> type)
product = col.findOne(filter) // product will be null again because the wrong resulting encoding {"_id": "..."}
println(product)

val id3 = WrappedObjectId<Product>(id.toString()) // correct and concise way to declare an ObjectId argument
product = col.findOneById(id3)  // product valid found because the correct resulting encoding of a WrappedObjectId type}
println(product)

Declaring an explicit bson serializer for StringId type will solve the problem, but I think that StringId type is made specifically for easy transport for an ObjectId in a backend <-> frontend environment.

So maybe the simpler solution is to use a specific type argument for the id, in your case just change: this.col<Product>().findOneById(id = id.toString().toId<Product>()) to this: this.col<Product>().findOneById(id = WrappedObjectId<Product>(id.toString())) or this: this.col<Product>().findOneById(id = ObjectId(id.toString()))

HTH,

best regards

urosjarc commented 1 year ago

I use kotlinx serialization and is huge pain because I somehow cant make a queries because of it. I use this 2 libs...

    implementation("org.litote.kmongo:kmongo-id-serialization:4.8.0")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.5.0")
urosjarc commented 1 year ago

@tfonrouge I can't use WrappedObjectId because kmongo-id-serialization library does not work as expected. I use kmongo-id-serialization in my domain layer and I will not bind domain layer to any library that will add to much dependency to it.

The problem is that this kind of queries...

   or(Narocnina::id_uporabnika eq id, Narocnina::id_placnika eq id)

Produce this results...

{"$or": [{"id_uporabnika": "641243bc9728e30c5931a204"}, {"id_placnika": "641243bc9728e30c5931a204"}]}

Which is simply wrong... The library does something wrong in this process.

urosjarc commented 1 year ago

@tfonrouge You were right, the problem is that both StringId and WrappedObjectId represents Id object and this is a problem because when I use newId() on object I do get WrappedObjectId but when I do queries I magicaly get StringId representation of Id. Its probably because deserialization on my ktor server when I'm getting objects from Db but I have no clue why?

tfonrouge commented 1 year ago

Hi @urosjarc ,

At this point I'm not sure what you are trying to accomplish, and perhaps it will be useful that you provides us with a small, self contained and running example in order to see the problem and find you a precise solution.

best regards

urosjarc commented 1 year ago

@tfonrouge Thank you you allready solved my problem... The problem is on my side I joust have to replace all .toId() calls to WrappedObjectId<Product>(id) and this is it...

I would really suggest that .toId() returns the same object as function newId() I think many people will fall to the same trap like me.

@tfonrouge I have one more request, can you please check out this issue #403 and tell if there are some alternatives to solve this issue.