Grokking MongoDB
Vote

0% completed

​

Introduction to BSON

Reading the Bytes

A Date Takes Less Space Than Its Text

BSON Is Not Always Smaller

BSON Data Types

Converting BSON Back to Text

BSON and JSON Compared

The same user takes 112 characters as JSON text and 96 bytes in the binary form that MongoDB uses. Here is the JSON text:

{"name":"John Doe","age":30,"email":"john.doe@example.com","isStudent":false,"createdAt":"2023-06-01T00:00:00Z"}

MongoDB does not store that text. It stores a binary encoding of the document, with a tag on each value that states its type. This format is called BSON, which stands for Binary JSON. It is used both to store documents on the server and to send them over the network, and it adds types that JSON does not have.

As a result, you normally never see BSON. You work with readable objects, and the driver (the library that connects your program or mongosh to the server) converts them in both directions.

A document is written as an object in the application, sent as BSON through the driver and the network, stored as BSON on the server, and returned as an object.
A document is written as an object in the application, sent as BSON through the driver and the network, stored as BSON on the server, and returned as an object.

Reading the Bytes

A short document shows how the format works. This user has three fields, and in BSON it takes 45 bytes:

{ name: "John Doe", age: 30, isStudent: false }
2d 00 00 00 total size: 45 bytes 02 6e 61 6d 65 00 type 02 (string), field name "name" 09 00 00 00 4a 6f 68 6e 20 44 6f 65 00 length 9, then "John Doe" and a null byte 10 61 67 65 00 type 10 (32-bit integer), field name "age" 1e 00 00 00 value 30 08 69 73 53 74 75 64 65 6e 74 00 type 08 (Boolean), field name "isStudent" 00 value false 00 end of document

Each number here is stored in binary, with the least significant byte first, which is why 45 appears as 2d 00 00 00. As a 32-bit integer, 30 takes four bytes, and reading it needs no conversion from text.

Now look at the length stored in front of "John Doe". Suppose MongoDB needs the isStudent field from this document. It reads the type and name of name, sees that the value is 9 bytes long, and skips those bytes without reading them. A JSON parser cannot do this, because it must read each character to find where a string ends. Moving through a document in this way is called traversal, and fast traversal is one of the main goals of BSON.

A Date Takes Less Space Than Its Text

The 96 bytes at the start of this lesson depend on one choice: createdAt was stored as a real date, using new Date("2023-06-01T00:00:00Z"). A BSON date takes 8 bytes. If you insert the same value as a string, the document takes 113 bytes, because the string needs 21 bytes for its characters and its final null byte, and 4 more for its length.

The size is the smaller benefit. A value stored with the date type sorts and compares as a date, whereas a string can only compare as text.

BSON Is Not Always Smaller

It would be wrong to conclude that BSON always saves space. The length prefixes and type tags take room, and small values can become larger than their text form. These counts come from encoding each document with a BSON library and measuring the JSON text:

DocumentJSON charactersBSON bytes
{"age":30}1014
{"scores":[1,2,3]}1839
{"n":2147483647}1612
The user above, with a real date11296

The array is the clearest case. BSON stores an array as a document whose field names are the positions "0", "1" and "2", and each small number therefore costs seven bytes. A stored document also has an _id field, which adds 17 bytes when it holds a generated identifier. The 96-byte user therefore takes 113 bytes once it is inserted, and the match with the string figure above is only a coincidence.

BSON accepts this extra space because the goal is speed. Binary numbers need no parsing, and the stored lengths allow skipping. The BSON specification itself says that BSON can use more space than JSON.

BSON Data Types

These are the types you can meet in a MongoDB document. The alias is the name that MongoDB uses for the type in queries and in the output of $type.

Data TypeAliasDescription
StringstringUTF-8 text.
32-bit IntegerintA signed whole number stored in 4 bytes.
64-bit IntegerlongA signed whole number stored in 8 bytes.
DoubledoubleA 64-bit floating point number, which can hold fractions but stores most of them approximately.
Decimal128decimalA 128-bit decimal floating point number.
Booleanbooltrue or false.
NullnullA null value.
ArrayarrayAn ordered list of values.
ObjectobjectA nested document.
Binary DatabinDataAn array of bytes.
ObjectIdobjectIdA 12-byte identifier, usually for _id.
DatedateMilliseconds since January 1, 1970 (the Unix epoch), in 8 bytes.
Regular ExpressionregexA regular expression pattern.
JavaScriptjavascriptJavaScript code stored as text.
JavaScript with ScopejavascriptWithScopeJavaScript code with a set of variables. Deprecated.
TimestamptimestampA special timestamp for internal MongoDB use.
Min KeyminKeyA value that compares lower than all other values.
Max KeymaxKeyA value that compares higher than all other values.

Three further types exist only for old data, and all three are deprecated: undefined, dbPointer and symbol.

Converting BSON Back to Text

Plain JSON cannot represent these extra types, and a date becomes a plain string when it is converted, with its type lost. MongoDB therefore has an extended form of JSON that records the type inside the text. In mongosh, EJSON.stringify produces it:

EJSON.stringify({ name: "John Doe", createdAt: new Date("2023-06-01T00:00:00Z") })
{"name":"John Doe","createdAt":{"$date":"2023-06-01T00:00:00Z"}}

The result is still valid JSON, and a program that understands this form can restore the date.

BSON and JSON Compared

AspectBSONJSON
FormatBinaryText
SizeLarger or smaller than the JSON text, because of type tags, lengths and binary valuesOne character for each character of text
Encoding and decodingFast: numbers need no parsingSlower: all values must be parsed from text
Data typesAdds dates, binary data, several number types and moreSix types only
TraversalLengths allow a reader to skip valuesA reader must read each character
Human readabilityNeeds a tool to readReadable directly

Reading Progress

0%


Vote for new content

On This Page

Reading the Bytes

A Date Takes Less Space Than Its Text

BSON Is Not Always Smaller

BSON Data Types

Converting BSON Back to Text

BSON and JSON Compared