MongoDB ObjectId Data Type – Guidelines

Today in this article, we will see more details about MongoDB ObjectId Data type.
Updated October 2026: a section on string-versus-ObjectId query bugs, a C# id modelling section, corrected ObjectId layout and an FAQ.
ObjectId is the 12-byte element consisting of a timestamp value, a random value, and an incremental counter.
Today in this article, we will cover below aspects,
MongoDB _id field as ObjectId
MongoDB uses the _id field as ObjectId this Id has few behavioral characteristics and it is explained below, A 4-byte timestamp is a UNIX timestamp.
- MongoDB database automatically generates _id the field for insertion.
- This field is unique for every document in the collection, except in time series collections, which don’t require a unique _id.
- _id field is automatically indexed and the index is unique.
It represents the ObjectId’s creation, measured in seconds since the Unix epoch. for more details, please visit here.

MongoDB ObjectId fields details
ObjectId is the 12-byte element consisting of a timestamp value, a random value, and an incremental counter.

- A 4-byte timestamp is a UNIX timestamp. It represents the ObjectId’s creation timestamp, measured in seconds since the Unix epoch.
If interested to know how to get timestamp using these 4 bytes, please visit the below article,
- A 5-byte random value is generated once per process. This random value is unique to the machine and process.
- A 3-byte incrementing counter, initialized to a random value.
References:
Do you have any comments or ideas or any better suggestions to share?
Please sound off your comments below.
Happy Coding !!
Please bookmark this page and share it with your friends. Please Subscribe to the blog to receive notifications on freshly published (2026) best practices and guidelines for software design and development.
Why a query by _id returns nothing: ObjectId vs string
A query by _id returns nothing when you filter with a string, because the stored value is an ObjectId and BSON treats the two as different types. The 24-character text looks identical, but it never matches.
Why doesn’t a string match an ObjectId?
BSON compares type as well as value. The string "507f1f77bcf86cd799439011" and the ObjectId with the same hex digits are two different values, so an equality filter on one never finds the other. MongoDB raises no error. You just get an empty result.
In plain English: It’s like a shoe size of 42 and bus route 42. The digits match, but they’re different kinds of thing, so you can’t swap one for the other.
How do you query by _id correctly in Node.js?
Validate the input, then convert it before it reaches the filter:
- Validate: confirm the string is exactly 24 hex characters.
new ObjectId()throws on malformed input, so reject bad values first and return a clean 400 instead of a server error. - Wrap: pass
new ObjectId(idString)in the_idfilter, not the raw string. - Compare with .equals():
===checks object identity in JavaScript, so two ObjectIds with the same bytes still compare as different. Usea.equals(b).
How should you store references to another document?
Store a reference to another document’s _id as an ObjectId, not as its string form. Otherwise every join or lookup has to convert types, and one missed conversion gives you the empty-result problem again.
If you only have the string, convert it inside the query or aggregation with $convert and to: "objectId". Treat that as a workaround for existing data. Fixing the stored type is the better long-term answer.
Can you trust _id order and the embedded timestamp?
Not completely. ObjectIds are only approximately ordered by creation time, so don’t treat _id order as a strict insertion sequence. Several clients can generate ids within the same second, and the counter and random bytes decide the order among them.
The embedded timestamp records when the id was generated, which isn’t always when the record was really created. If you may migrate, re-insert or bulk-load documents, store an explicit createdAt field and use that for reporting and sorting.
Frequently asked questions
Why does my MongoDB query by _id return nothing when the string looks right?
The stored _id is an ObjectId and you are filtering with a string, so the types differ and nothing matches. Convert the string to an ObjectId first (after checking it is 24 hex characters), or map the property with BsonRepresentation(BsonType.ObjectId) in C#. With the attribute on a string property, the C# driver converts between the string and the stored ObjectId for you, so your filters can keep using the string.
Is a MongoDB ObjectId strictly ordered and guaranteed unique?
ObjectIds are designed to be unique by combining a timestamp, a per-process random value and a counter. They are only approximately ordered by creation time, because clients generate them independently, so sort by an explicit field if strict order matters. Two clients creating documents in the same second can produce ids that sort differently from the real insertion sequence.
How do I compare two ObjectIds correctly in JavaScript?
Use .equals(), because === compares object identity and returns false for two separate objects holding the same value. Convert both to strings with toString() if you need a plain comparison. For example, a.equals(b) and a.toString() === b.toString() both work for ids that hold the same 12 bytes.
How do I get the creation time from an ObjectId?
Call getTimestamp() in mongosh or the driver; it reads the 4-byte seconds-since-epoch value. It reflects when the id was generated, so keep a separate createdAt field if documents may be migrated or re-inserted. The value has one-second resolution, so it can’t tell you the order of documents created within the same second.
