BSON
MongoDB stores documents as BSON — Binary JSON — not plain text JSON. BSON is a binary-encoded serialization format that extends JSON's model with additional data types and metadata designed for fast traversal, efficient storage, and unambiguous typing.
Why Binary Instead of Text JSON?
Speed of parsing. BSON encodes the length of each element up front, so a driver can skip over fields without scanning byte-by-byte the way a JSON text parser must.
Richer types. Plain JSON only has strings, numbers, booleans, null, arrays, and objects. BSON adds dates, binary data, decimals with exact precision, and a dedicated ID type (
ObjectId), among others.Traversability. Because each BSON value is length-prefixed, MongoDB can jump directly to a field deep in a document without re-parsing everything before it.
Efficient on-disk storage compared to storing equivalent text JSON, particularly for numeric-heavy documents.
JSON vs BSON
Aspect | JSON | BSON |
|---|---|---|
Format | Human-readable text | Binary |
Types | string, number, boolean, null, array, object | All JSON types plus Date, ObjectId, Binary, Decimal128, RegExp, Timestamp, Int32/Int64 distinction, and more |
Numbers | Single "number" type (double precision in most parsers) | Distinguishes Int32, Int64, Double, and exact-precision Decimal128 |
Traversal | Must scan/parse sequentially | Length-prefixed — can skip fields |
Where you see it | REST APIs, config files, | On disk, over the wire, in |
Common BSON Types
BSON Type | mongosh Example | Notes |
|---|---|---|
Double | 3.14 | Default for decimal numbers typed in mongosh |
String | "hello" | UTF-8 encoded |
Object | { a: 1 } | An embedded document |
Array | [1, 2, 3] | Ordered list of any BSON values |
Binary data | BinData(0, "...") | Raw bytes — files, hashes, encrypted blobs |
ObjectId | ObjectId("64f1a2b3...") | 12-byte unique identifier, default |
Boolean | true / false | |
Date | ISODate("2024-01-15") | Milliseconds since Unix epoch, UTC |
Null | null | Explicit "no value" — distinct from a missing field |
32-bit integer | NumberInt(42) | Whole numbers that fit in 4 bytes |
Timestamp | Timestamp(1690000000, 1) | Internal — used by the oplog, not app data |
64-bit integer | NumberLong(42) | Whole numbers that fit in 8 bytes |
Decimal128 | NumberDecimal("9.99") | Exact base-10 precision — use for money |
Regular Expression | /^abc/i | Stored pattern, usable in queries |
Type Numbers and Aliases
Every BSON type has both a numeric code and a string alias. The $type query operator accepts either form, which is useful when auditing a collection for inconsistent field types.
Type codes and aliases
// 1 = "double" 8 = "bool" 18 = "long"
// 2 = "string" 9 = "date" 19 = "decimal"
// 3 = "object" 10 = "null" 127 = "maxKey"
// 4 = "array" 11 = "regex" -1 = "minKey"
// 5 = "binData" 16 = "int"
// 7 = "objectId" 17 = "timestamp"
// Find documents where "price" was mistakenly stored as a string
db.products.find({ price: { $type: "string" } })
// Same query using the numeric code instead of the alias
db.products.find({ price: { $type: 2 } })
// "number" is a special alias that matches double, int, long, AND decimal
db.products.find({ price: { $type: "number" } })Inspecting BSON Types in mongosh
Distinguishing look-alike values
db.events.insertMany([
{ label: "A", when: new Date() }, // BSON Date
{ label: "B", when: "2024-01-15" }, // plain string — NOT a Date!
{ label: "C", count: 10 }, // Double (mongosh default)
{ label: "D", count: NumberInt(10) }, // 32-bit integer
{ label: "E", count: NumberLong(10) } // 64-bit integer
])
// This query only matches documents where "when" is a real BSON Date
db.events.find({ when: { $type: "date" } })
// → returns only document "A""2024-01-15") sorts and compares lexicographically, not chronologically, and will not match $type: "date" queries or date-range queries. Always insert dates through your driver's native date type or new Date() / ISODate() in mongosh.Size Considerations
A single document is capped at 16 MB of BSON — this is the format's practical limit, not an arbitrary MongoDB rule.
Binary data (
BinData) is base64-encoded when displayed in JSON tools likemongoexport, which inflates its apparent size by roughly 33% — the actual BSON storage is the raw byte count.Field names count toward document size on every single document — extremely verbose key names across millions of documents add up. Short, consistent field names save real storage.
Use
Object.bsonsize(doc)inmongoshto measure the exact BSON byte size of any document you have in hand.
mongosh, mongoexport converts BSON to Extended JSON — a text JSON representation that wraps non-JSON-native types in tagged objects, e.g. { "$oid": "64f1a2b3..." } for an ObjectId. This lets you round-trip BSON through plain-text tooling without losing type information.