Filter concepts by levelShowing all levels.

MongoDB · BSON and Data Types

BSON and Data Types

The type list, BSON vs. JSON, and the numeric and identifier types worth knowing well

  • Four number typesdouble, int32, int64, decimal128 — not one
  • BSON is binaryJSON-like text is only how it is displayed
  • ObjectId = 12 bytestimestamp + random + counter, unique without coordination
  • Date is a UTC instantno timezone stored — apply one only at display

1.BSON types

BSON has more types than JSON: alongside string, boolean, null, object and array, it has distinct number types (double, int32, int64, decimal128), a real date type, ObjectId, binary data, regex, and a couple of rarely used legacy types.

Key features

  • string, boolean, null, object and array exist in both JSON and BSON
  • double, int32, int64 and decimal128 are four distinct BSON number types
  • date, timestamp, objectId, binary and regex have no JSON equivalent
  • timestamp is an internal type for replication, not for application dates — use date
  • JavaScript/code types exist for legacy server-side scripting, rarely used today

The BSON type list

The BSON type list
TypeExampleJSON has it?
string"Dune"yes
booleantrueyes
nullnullyes
object{ "k": "v" }yes
array[1, 2, 3]yes
double3.14no — JSON has one generic number
int32NumberInt(42)no
int64NumberLong(42)no
decimal128NumberDecimal("9.99")no
dateISODate("2026-01-01")no
timestampinternal, replication useno
objectIdObjectId("65f1...")no
binaryBinData(0, "...")no
regex/^abc/no
JSON's one number type vs. BSON's four

JSON

  • One generic "number"
  • No fixed width
  • No exact decimal type

BSON

  • double, int32, int64, decimal128
  • Each has a fixed, known width
  • decimal128 is exact for money
  • JSON
    • One generic "number"
    • No fixed width
    • No exact decimal type
  • BSON
    • double, int32, int64, decimal128
    • Each has a fixed, known width
    • decimal128 is exact for money

Command

json
{
  "text": "value",           // string
  "count": NumberInt(1),     // int32
  "big": NumberLong(1),      // int64
  "price": NumberDecimal("1.00"),  // decimal128
  "when": ISODate("2026-01-01")    // date
}

BSON has more types than JSON: double/int32/int64/decimal128 instead of one number, plus date, objectId, binary, regex. Use decimal128 for money.

2.BSON vs. JSON

JSON is human-readable text; BSON is the binary format MongoDB actually stores and transmits. BSON is a superset — richer types, but not readable in a text editor without a tool that decodes it.

Key features

  • BSON is binary; JSON is text — that makes BSON faster to parse and traverse
  • Every BSON value is length-prefixed, so a reader can skip fields without scanning
  • BSON adds types (date, objectId, binary, decimal128, ...) JSON cannot express
  • mongosh and drivers show documents as JSON-like text — a rendering, not storage
  • Round-tripping through plain JSON.parse/stringify silently loses BSON-only types

BSON vs. JSON

BSON vs. JSON
PropertyJSONBSON
Formattextbinary
Human-readableyes, directlyno — needs a decoder
Traversalmust scan/tokenize textlength-prefixed, can skip fields
Type richnessstring/number/boolean/null/object/arrayadds date, objectId, binary, decimal128, regex, ...
Where it's used hereshell/driver display, this app's code sampleswhat MongoDB actually stores and sends
What you type vs. what is stored

JSON-like text

what you write/read

BSON

binary, what's stored

on disk / wire

transmitted this way

  1. JSON-like text — what you write/read
  2. BSON — binary, what's stored
  3. on disk / wire — transmitted this way

Command

text
// mongosh and drivers render BSON as JSON-like text — this is a display convenience,
// not the on-disk or on-wire representation.

BSON is binary and what MongoDB actually stores/sends; JSON-like text is only how it is displayed. BSON has richer types JSON cannot express.

3.BSON size and serialization

Every BSON document has a hard 16 MB size limit, and every field carries a small overhead — its name and a type byte are stored per field, not just per document. Both affect how a document should be shaped.

Key features

  • A single BSON document is capped at 16 MB
  • Each field stores its own name as part of the document, repeated per document
  • Deeply nested or highly repetitive field names add up across many documents
  • GridFS (later in this topic) exists to store objects larger than 16 MB
  • Serialization cost scales with document size and field count, not just bytes

What counts toward document size

What counts toward document size
ContributorExampleNote
Field names"createdAt" repeated per documentshort field names measurably reduce total size at scale
Scalar values42, "text", trueproportional to content
Embedded documentsnested objectseach nested field adds its own name overhead
Arrayslarge arrays of documentsunbounded arrays are the most common way to hit 16 MB

Command

text
// No syntax to run — a size/serialization constraint to design around, not an API call.

A BSON document is capped at 16 MB; field names are stored per document, so short names and bounded arrays measurably matter at scale.

4.Integer types and numeric precision

int32 holds roughly ±2.1 billion, int64 far more, and double trades exactness for range. MongoDB compares numbers by value across these types, but which type a driver picks for a literal is not always obvious.

Key features

  • int32 range is roughly ±2.1 billion (2^31); int64 is far larger
  • double is binary floating point — inexact for most decimal fractions
  • MongoDB compares numbers across types by value ($eq: 42 matches any type)
  • Arithmetic can silently promote a type, e.g. int32 overflowing into double
  • A driver's default for a numeric literal is per-driver, not a MongoDB rule

The four number types compared

The four number types compared
TypeRange/precisionBest for
int32±2.1 billion, exactsmall counters, flags
int64much larger, exactIDs, large counters, timestamps in millis
double~15-17 significant digits, binarymeasurements, scores, anything tolerant of rounding
decimal12834 significant decimal digits, exact base-10money, exact decimal arithmetic

Command

json
{ "small": NumberInt(1), "big": NumberLong(1), "measured": 1.5, "exact": NumberDecimal("1.50") }

int32/int64 are exact but range-limited; double is binary and can round; decimal128 is exact base-10. Queries compare by value across types.

5.Decimal128

Decimal128 stores an exact base-10 decimal, unlike double, which is binary and cannot represent most decimal fractions exactly. Use it for money and anywhere small rounding errors are unacceptable, even if they seem tiny.

Key features

  • Decimal128 stores an exact base-10 value, with 34 significant digits
  • double is binary floating point — most decimal fractions are only approximated
  • Money, invoicing, and any exact-equality decimal comparison are the classic use cases
  • Decimal128 costs more storage/computation than double — not a default choice
  • The decimal128 constructor must be used explicitly; plain literals default to double

When to reach for each numeric type

When to reach for each numeric type
Use caseTypeWhy
Item price, invoice totaldecimal128exact — no compounding rounding error across many transactions
Sensor reading, average scoredoublesmall rounding error is acceptable, range matters more
Row counter, user IDint32 / int64exact integers, no fractional part needed
Exact scientific/financial constantdecimal128precision matters more than storage cost

Command

json
{ "price": NumberDecimal("19.99") }

Use decimal128 (NumberDecimal) for money and exact decimals; double is fine where small rounding error is acceptable.

6.BSON Date

A BSON date stores a single instant in time — milliseconds since the Unix epoch, UTC — with no timezone attached. Any timezone a user sees is applied when the application displays the value, not stored alongside it.

Key features

  • BSON date is milliseconds since 1970-01-01T00:00:00Z (the Unix epoch), UTC
  • No timezone is stored with the value — it is always one absolute instant
  • Application code converts to a local timezone only for display or input parsing
  • Storing local time with no timezone as a plain string invites ambiguity
  • Comparing and sorting dates is a straightforward numeric comparison

Where a timezone lives

Where a timezone lives
LayerHas a timezone?Responsibility
BSON date valueno — UTC instant onlystore the absolute moment
Application display codeyes, chosen at render timeconvert UTC to the viewer's local time
User input formyes, the user's local timeconvert local input back to UTC before storing

Command

json
{ "createdAt": ISODate("2026-01-01T00:00:00Z") }

BSON date is a UTC instant, no timezone stored. Convert to local time only at display/input, in application code.

7.ObjectId structure

An ObjectId is a 12-byte value MongoDB generates as the default _id: a 4-byte timestamp, 5 bytes of random value, then a 3-byte counter — unique without coordination, and roughly sortable by creation time.

Key features

  • ObjectId is 12 bytes: 4-byte timestamp + 5-byte random value + 3-byte incrementing counter
  • It is generated client-side (by the driver) by default, before the insert is sent
  • Two ObjectIds generated in the same second still differ via the random value and counter
  • ObjectIds sort roughly by creation time, because the timestamp is the leading bytes
  • getTimestamp() extracts the embedded creation time from an existing ObjectId

ObjectId's 12 bytes

ObjectId's 12 bytes
BytesHoldsPurpose
0–3Unix timestamp (seconds)when it was generated — makes it roughly sortable
4–8random value, per processuniqueness without central coordination
9–11incrementing counteruniqueness within the same second, same process
ObjectId's three parts

12 bytes total

timestamp

4 bytes

random

5 bytes

counter

3 bytes

  • 12 bytes total
    • timestamp — 4 bytes
    • random — 5 bytes
    • counter — 3 bytes

Command

text
ObjectId()                    // generates a new one
ObjectId("65f1...").getTimestamp()   // extracts its creation time

ObjectId is 12 bytes: timestamp + random + counter — unique without coordination. _id defaults to it but is not required to be one.

Remember

  • BSON is binary and what MongoDB actually stores/sends; JSON-like text is only a rendering of it.
  • Use decimal128 (NumberDecimal) for money — double is binary floating point and can round.
  • ObjectId is 12 bytes: timestamp + random + counter — unique without a central counter.
  • A BSON document is capped at 16 MB; field names are stored per document.

Pro tip

  • Queries compare numbers by value across int32/int64/double, so a field's exact stored type rarely affects a $eq match.
  • Store dates as a real date value, never as a timezone-less string — convert to local time only at display.

Next up

  • Documents and Collections
  • CRUD Operations
  • Query Language

Sources

  • BSON Types — mongodb.com/docs/manual/reference/bson-types/
  • ObjectId — mongodb.com/docs/manual/reference/method/ObjectId/
  • Checked 2026-08-20

Spotted a mistake on this page? Tell us.