ObjectId: Structure, Generation, and Querying
ObjectId is a BSON type commonly used for MongoDB document identifiers. When an inserted document does not specify _id, MongoDB drivers commonly generate an ObjectId automatically unless the collection or insertion workflow uses another supported identifier strategy.
Example:
{
_id: ObjectId("65a000000000000000000001"),
name: "Arun"
}
Why _id exists
Each document in a collection must have a unique _id value. MongoDB creates a unique _id index for ordinary collections to enforce this requirement.
ObjectId is the conventional default, but _id can use other supported BSON types, including strings, numbers, and UUID representations. Your choice should reflect the application's requirements.
ObjectId structure
A standard ObjectId is 12 bytes, conventionally represented as 24 hexadecimal characters.
Its components are:
- A 4-byte timestamp representing seconds since the Unix epoch.
- A 5-byte process-unique random value.
- A 3-byte counter.
This structure supports distributed generation with a low probability of collisions. It should not be treated as a mathematically absolute guarantee that collisions are impossible.
Generating an ObjectId
In mongosh:
const id = ObjectId();
print(id);
In a Node.js application using the MongoDB driver:
import { ObjectId } from "mongodb";
const id = new ObjectId();
console.log(id.toHexString());
The exact import path depends on the package and module system used by your project.
Querying by ObjectId
const id = ObjectId("65a000000000000000000001");
db.users.findOne({ _id: id });
If _id is stored as an ObjectId, querying it with the corresponding hexadecimal string does not ordinarily match the stored ObjectId.
This distinction is common when handling route parameters in a web application. An API may receive an ID as a string, but the backend must convert it to an ObjectId when the database field uses that BSON type.
ObjectId timestamp extraction
In mongosh, an ObjectId provides access to its embedded timestamp:
const id = ObjectId();
id.getTimestamp();
The timestamp has only second-level resolution. It is not a reliable substitute for a dedicated createdAt field when an application requires precise event times or explicit business semantics.
ObjectId is not an authorization mechanism
An ObjectId can be difficult to guess in many contexts, but its format does not provide authorization. Every endpoint that accesses a resource should verify that the authenticated caller has permission to access that resource.
db.objectIdExamples.insertOne({
name: "Arun",
createdAt: new Date()
});
const document = db.objectIdExamples.findOne({ name: "Arun" });
const id = document._id;
print("ObjectId:", id);
print("Hex string:", id.toHexString());
print("Timestamp:", id.getTimestamp());
// Query using the BSON ObjectId.
db.objectIdExamples.findOne({ _id: id });
// Convert a known hexadecimal string into an ObjectId.
db.objectIdExamples.findOne({
_id: ObjectId(id.toHexString())
});_id, retrieves the generated identifier, and inspects its hexadecimal representation and embedded timestamp. ObjectId values and their string representations are different types. The timestamp embedded in ObjectId has second-level resolution and should not replace a dedicated creation timestamp when greater precision is needed.- ObjectId is a BSON type commonly used for document identifiers.
- A standard ObjectId contains 12 bytes and is represented by 24 hexadecimal characters.
- MongoDB requires a unique
_idvalue for each document in a collection. - A hexadecimal string must be converted to ObjectId when querying an ObjectId field.
- ObjectId is not a replacement for authorization or a precise business timestamp.
A document stores `_id` as an ObjectId. Why might `{ _id: "65a000000000000000000001" }` fail to find it?