gyeo
gyeo

Reputation: 63

How is Mongo ObjectId Compared Internally?

TLDR: There is no difference between ObjectId("LowerCaseHexString") and ObjectId("UpperCaseHexString") when running Mongo Shell commands.

Just a careless mistake on my part...

Original Qn:

I've just noticed some very quirky behavior while executing a couple of commands, to remove some array elements in a sub-document, in the Mongo Shell.

This has to do with whether an uppercase or lowercase hexadecimal string is used while constructing object ids i.e. ObjectId("UpperOrLowerCaseString")

Firstly, both these queries return the document:

db.users.findOne({"id":1, "SubDoc._id":{$lt: ObjectId("54B4EF540000000000000000")}})
db.users.findOne({"id":1, "SubDoc._id":{$lt: ObjectId("54b4ef540000000000000000")}})

And both of these do not (note that the $lt has been replaced with $gt):

db.users.findOne({"id":1, "SubDoc._id":{$gt: ObjectId("54B4EF540000000000000000")}})
db.users.findOne({"id":1, "SubDoc._id":{$gt: ObjectId("54b4ef540000000000000000")}})

Which is expected. HOWEVER when trying to $pull elements from the sub-document, the following query works:

db.users.update(
    {"id":1}, 
    {$pull:
        {"SubDoc":
            {_id: {$lt: ObjectId("54b4ef540000000000000000")}}
        }
    }
)

While this does not (note the change from lowercase to uppercase):

db.users.update(
    {"id":1}, 
    {$pull:
        {"SubDoc":
            {_id: {$lt: ObjectId("54B4EDC40000000000000000")}}
        }
    }
)

Interestingly enough this works (note the $lt replaced with $gt):

db.users.update(
    {"id":1}, 
    {$pull:
        {"SubDoc":
            {_id: {$gt: ObjectId("54B4EDC40000000000000000")}}
        }
    }
)

Which leads me to believe that in the case of update, the actual string values are being used to compare the object ids (the lowercase string being larger due to byte representation).

Where as in the case of findOne, this does not seem to be the case i.e. Object Ids are converted to their 12 byte form before being compared.

Hopefully this is not a mistake on my part (quite prone to those)...

But anyone care to shed some light? Especially on the inconsistency of behaviors between my findOne and update queries?

And if indeed the full string values are bring used for comparison, then isn't that pretty worrying performance wise? I.e. comparing 24 bytes each time instead of just 12?

Upvotes: 2

Views: 1216

Answers (1)

JohnnyHK
JohnnyHK

Reputation: 311885

There's no difference in the ObjectIDs that result from using lower-case or upper-case versions of the same hex value. You're seeing different results because you're using different hex values in the two cases:

ObjectId("54b4ef540000000000000000")
...            ^^
ObjectId("54B4EDC40000000000000000")

Upvotes: 4

Related Questions