Learn what BSON is, the data types MongoDB stores (strings, numbers, dates, arrays, ObjectId and more) and the rules every document must follow.

Bishal Biswas
Self Learner
What you will learn
By the end of this lesson you will know:
- What BSON is and why MongoDB uses it instead of plain JSON
- The data types you’ll use every day: strings, numbers, booleans, dates, arrays, embedded documents, null and ObjectId
- The difference between the number types (
Int32,Double,Long,Decimal128) - How to check a field’s type with
$type - The rules every document must follow (size limit,
_id, field names)
From JSON to BSON
You already know JSON. It’s the text format used all over the web:
{ "name": "Notebook", "price": 49, "inStock": true }
JSON is great, but it has two problems for a database:
- It has very few types. Just strings, numbers, true/false, null, arrays and objects. There’s no date type and no way to say “this number is an integer” or “this is a precise decimal”.
- It’s text. Text is slow for a computer to scan and parse, because it has to read every character to find where a value ends.
So MongoDB stores documents as BSON, short for Binary JSON. BSON keeps the same shape as JSON, but:
- it’s stored as compact binary data, which is fast to read and write
- it adds extra types, like dates, several kinds of numbers, ObjectId and binary data
Think of it like this: JSON is a handwritten recipe card: easy for people to read. BSON is the same recipe typed into a well-organised database form, with labelled boxes for “date”, “whole number” and “exact amount”. Easier for the computer, and nothing gets misread.
You never write raw BSON yourself. You type JavaScript-like documents in mongosh or your app, and the driver converts them to BSON behind the scenes.
The everyday types
Here is one document that uses most of the common types. Each field is a different type:
use shop
db.typesDemo.insertOne({
name: "Headphones", // String
price: 1999, // Int32 (a whole number)
rating: 4.5, // Double (a decimal number)
wireless: true, // Boolean
tags: ["audio", "wireless"], // Array
dimensions: { widthCm: 18, heightCm: 20 }, // Embedded document (Object)
discount: null, // Null
addedAt: new Date("2026-10-25T08:00:00Z"), // Date
supplierId: ObjectId("6718a2f1c3b4e5d6f7a8b9c0") // ObjectId
})
Let’s go through them one by one.
String
Text, always inside quotes. BSON strings are UTF-8, so any language works: "Asha", "राहुल", "東京", "café".
{ name: "Gel Pen", city: "Kolkata" }
Boolean
Just true or false, without quotes.
{ isMember: true, wireless: false }
Warning:
"true"(with quotes) is a string, not a boolean. A query for{ isMember: true }will not find a document whereisMemberis"true".
Numbers (there are four kinds!)
BSON has four number types. In daily work you’ll mostly meet the first two.
| Type | What it holds | How to write it in mongosh | Good for |
|---|---|---|---|
| Int32 (“int”) | Whole numbers up to about ±2.1 billion | 49 or Int32(49) |
Counts, quantities, stock |
| Double (“double”) | Numbers with decimals (approximate) | 4.5 or Double(4) |
Ratings, measurements |
| Long (“long”) | Very large whole numbers | Long("9007199254740993") |
Big counters, IDs from other systems |
| Decimal128 (“decimal”) | Exact decimal numbers | Decimal128("19.99") |
Money, when every cent must be exact |
How does mongosh pick a type when you just type a number?
- If it’s a whole number that fits in 32 bits, mongosh stores it as Int32. So
price: 49is an Int32. - Otherwise (for example
4.5, or a whole number bigger than about 2.1 billion), it stores a Double.
Why does Decimal128 exist? Because doubles can’t store some decimals exactly. Try this in mongosh:
0.1 + 0.2
// → 0.30000000000000004
That tiny error is normal for doubles in every programming language. For prices with paise or cents, it can add up. Decimal128 avoids it:
db.typesDemo.insertOne({ item: "Gift Card", amount: Decimal128("19.99") })
Note: In this course, prices are whole numbers (like
49or1999), so plain numbers are fine. In a real shop that needs exact decimal prices, useDecimal128, or store prices in the smallest unit (for example4999paise instead of49.99rupees).
Good news: all number types compare by their value. A query for { price: 49 } matches 49 whether it’s stored as Int32, Long or Double.
Date
Dates are stored as the number of milliseconds since 1 January 1970, in UTC (world standard time). mongosh shows them wrapped in ISODate(...).
Create dates with new Date() or ISODate():
new Date() // right now
// → ISODate("2026-10-25T08:00:00.000Z")
new Date("2026-01-15") // a specific day
ISODate("2026-01-15T10:30:00Z") // a specific moment
Warning:
Date()withoutnewreturns a plain string, not a date! Always writenew Date(). Dates stored as strings can’t be compared or sorted correctly as dates.
typeof Date() // → string (wrong for storing)
typeof new Date() // → object (a real Date)
The Z at the end means UTC. MongoDB always stores dates in UTC. Your app converts them to local time (like India Standard Time) when showing them to users.
Array
An ordered list of values inside square brackets. Values can be any type, even documents.
{ tags: ["paper", "school"] }
{ scores: [72, 88, 95] }
{ items: [ { name: "Notebook", qty: 2 }, { name: "Gel Pen", qty: 5 } ] }
Arrays are one of MongoDB’s superpowers. A query for { tags: "school" } finds every document whose tags array contains "school". You’ll learn much more in Working with Arrays.
Embedded document (Object)
A document inside a document, in curly braces. Use it to group related fields.
{
name: "Asha Roy",
address: { street: "12 Park Street", city: "Kolkata", pin: "700016" }
}
You reach inside with dot notation, such as "address.city". See Embedded Documents and Dot Notation.
Tip: Store postal codes and phone numbers as strings, not numbers. They’re labels, not amounts, and some start with
0, which a number would drop.
Null
null means “this field exists, but has no value”.
{ name: "USB Cable", rating: null }
This is different from a missing field, where the field isn’t there at all:
{ name: "Notebook" } // no rating field at all
You’ll see in Querying with find and Filters that a query for { rating: null } matches both cases.
ObjectId
A 12-byte unique ID, shown as 24 hexadecimal characters:
ObjectId("6718a2f1c3b4e5d6f7a8b9c0")
MongoDB uses it as the default _id value. It gets a whole lesson of its own next: ObjectId and the _id Field.
Less common types
You won’t need these often, but it’s good to recognise them:
| Type | What it is |
|---|---|
| Binary data (“binData”) | Raw bytes, such as a small image thumbnail, or a UUID created with UUID() |
| Regular expression (“regex”) | A text pattern, like /^note/i |
| Timestamp | An internal type MongoDB uses for replication. For your own dates, always use Date. |
| MinKey / MaxKey | Special values that sort lower / higher than everything else |
Checking a field’s type with $type
Since one collection can hold different types in the same field, it’s useful to ask “which documents store this field as a string?”. The $type query operator does that.
Let’s create a few documents with the same field stored as different types:
db.typesDemo.insertMany([
{ item: "A", value: 1 }, // Int32
{ item: "B", value: 1.5 }, // Double
{ item: "C", value: Long("1") }, // Long
{ item: "D", value: Decimal128("1.50") }, // Decimal128
{ item: "E", value: "1" } // String!
])
Now find only the strings:
db.typesDemo.find({ value: { $type: "string" } })
// → [ { _id: ObjectId("..."), item: "E", value: "1" } ]
Find any kind of number with the special alias "number":
db.typesDemo.find({ value: { $type: "number" } }, { item: 1, _id: 0 })
// → [ { item: "A" }, { item: "B" }, { item: "C" }, { item: "D" } ]
Useful type names for $type:
| Alias | Type |
|---|---|
"string" |
String |
"int", "long", "double", "decimal" |
The four number types |
"number" |
Any of the four number types |
"bool" |
Boolean |
"date" |
Date |
"array" |
Array |
"object" |
Embedded document |
"null" |
Null |
"objectId" |
ObjectId |
Tip:
$typeis a great detective tool for messy data. For example,db.products.find({ price: { $type: "string" } })finds products where someone saved the price as text by mistake.
The rules every document follows
MongoDB is flexible, but a few rules always apply.
1. Every document has an _id
Every document in a normal collection must have a unique _id field. If you don’t give one, MongoDB creates an ObjectId for you. The _id is always stored as the first field.
2. Maximum size: 16 MB
One document can be at most 16 mebibytes (about 16.7 million bytes). That’s a lot of text, roughly a few thousand pages of a book. If you hit this limit, it usually means a design problem, like an array that keeps growing forever. For big files (videos, large PDFs), MongoDB offers GridFS for Large Files.
3. Nesting depth: at most 100 levels
Documents inside documents inside documents… can go up to 100 levels deep. Real documents rarely go past 3 or 4. (On Atlas free clusters, the limit is 50 levels.)
4. Field name rules
- Field names are strings and are case-sensitive:
Nameandnameare two different fields. - Each field name must be unique within its document.
_idis reserved for the document’s ID.- Field names may technically contain dots (
.) and dollar signs ($), but avoid them. Dots clash with dot notation and$clashes with operators.
5. Field order is kept
MongoDB keeps your fields in the order you wrote them (with _id moved to the front). You normally don’t need to care about field order, except when you compare whole embedded documents, which you’ll see in a later lesson.
A note on Extended JSON
Since plain JSON has no Date or ObjectId type, MongoDB tools use Extended JSON when they need to write BSON as text, for example when exporting to a .json file. Special types become small objects with a $ key:
{
"_id": { "$oid": "6718a2f1c3b4e5d6f7a8b9c0" },
"name": "Notebook",
"addedAt": { "$date": "2026-10-25T08:00:00Z" },
"amount": { "$numberDecimal": "19.99" }
}
You’ll see this format in Compass’s JSON view, in the VS Code extension, and in export files. Just remember: { "$oid": ... } means ObjectId and { "$date": ... } means Date.
Common mistakes
- Storing numbers or booleans as strings.
"49"and49are different. So are"true"andtrue. Queries, sorting and maths only work correctly with the right type. - Writing
Date()instead ofnew Date(). Withoutnewyou store a string, not a date. - Using doubles for money that needs exact decimals. Use
Decimal128, or store whole amounts in the smallest unit (paise/cents). - Storing phone numbers or PIN codes as numbers. Leading zeros disappear and you can’t do maths on them anyway. Use strings.
- Letting arrays grow without limit. A document can’t exceed 16 MB. Put unbounded lists (like every log entry ever) in their own collection.
Practice
- Write a document for a customer named Priya from Kochi who joined on 5 April 2026, is a member, has a phone number
"09876543210"and an address with a street and a city. - Which type should each field use: a product’s stock count, a product rating like 4.8, an exact bank balance, the date of an order?
- Write a query that finds every product whose
pricewas saved as text by mistake.
Show a possible answer
// 1.
db.customers.insertOne({
name: "Priya Nair",
city: "Kochi",
joinedAt: new Date("2026-04-05"),
isMember: true,
phone: "09876543210", // a string keeps the leading 0
address: { street: "4 Marine Drive", city: "Kochi" }
})
// 2.
// stock count → Int32 (whole number), e.g. 120
// rating 4.8 → Double
// bank balance → Decimal128("10450.75")
// order date → Date, e.g. new Date()
// 3.
db.products.find({ price: { $type: "string" } })
Key takeaways
- MongoDB stores documents as BSON, a fast binary version of JSON with more types.
- Everyday types: String, Boolean, numbers (Int32, Double, Long, Decimal128), Date, Array, embedded document, Null and ObjectId.
- Use
new Date()for dates (neverDate()), andDecimal128when decimals must be exact. $typefinds documents by a field’s type. The"number"alias matches any number type.- Every document has a unique
_id, can be up to 16 MB, and has case-sensitive field names.
Next lesson: ObjectId and the _id Field
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready