Data Types & BSON
🔢 Data Types & BSON
$jsonSchema validation — MongoDB's opt-in answer to Chapter 5's point that schema flexibility is a deliberate trade-off, not a lack of options.
BSON Types in Depth
| Type | Notes |
|---|---|
ObjectId | MongoDB's default unique identifier type |
String | UTF-8 text |
Int32 / Int64 | Whole numbers |
Double | Standard floating-point — the default for decimal literals |
Decimal128 | Exact decimal representation — see this chapter's money warning below |
Boolean | true / false |
Date | A real timestamp type — see the string-vs-Date gotcha below |
Array | An ordered list, can hold any mix of types, including nested arrays/objects |
Object | A nested (embedded) document |
Null | An explicit absence of a value |
ObjectId: MongoDB's Default Unique Identifier
An ObjectId is a 12-byte value made of a 4-byte creation timestamp, a 5-byte random value, and a 3-byte incrementing counter. One useful consequence: ObjectIds are roughly sortable by creation time, and the creation timestamp can be pulled directly out of an ObjectId with no separate stored field needed:
Extracting a Timestamp from an ObjectId
Dates: A Common Gotcha
MongoDB has a real Date BSON type (stored internally as milliseconds since the Unix epoch). A common mistake is storing a date as a plain string instead — "2026-07-06" — rather than an actual Date value.
Date-as-String vs. a Real Date
Stored as a String
{ createdAt: "2026-07-06" }
Range queries ($gt/$lt) compare strings lexicographically, character by character — which happens to work for consistently-formatted YYYY-MM-DD strings, but breaks the moment formats mix or times are involved.
Stored as a Real Date
{ createdAt: ISODate("2026-07-06") }
Range queries compare actual points in time correctly, regardless of formatting, time zones, or precision.
Arrays and Nested Documents
Arrays can hold any mix of types, including further nested arrays or objects — Chapter 5's embedding examples (addresses, comments) are really just arrays of nested documents. Nesting can go arbitrarily deep, but very deep nesting tends to hurt both query readability and performance — a practical reason to keep documents reasonably flat where possible, on top of Chapter 5's growth-based reasons for choosing embedding vs. referencing in the first place.
Decimal128: Precise Numbers for Money
The default numeric type for a decimal literal in mongosh is Double — ordinary binary floating-point, the same representation used in most programming languages, which cannot represent every decimal value exactly. Decimal128 stores an exact decimal value instead, avoiding the rounding errors floating-point math can introduce — worth using deliberately for anything involving money.
Schema Validation with $jsonSchema
MongoDB's opt-in mechanism for enforcing structure on a collection — required fields, expected types, and value constraints — directly addressing the "nothing stops inconsistent documents" trade-off from Chapter 1 and Chapter 5:
A Validator Requiring Structure
💻 Coding Challenges
Challenge 1: Pick the Right BSON Type
Recommend a BSON type for each: (a) a customer's account balance in dollars, (b) an order's placement timestamp, (c) a product's list of tags.
Goal: Practice matching a real field to the type that avoids known pitfalls (money, dates) covered in this chapter.
Challenge 2: Spot the Date-as-String Bug
A query db.orders.find({ placedAt: { $gt: "2026-02-01" } }) is meant to find orders placed after February 1st, but returns some orders from January and misses some from March. Explain the likely root cause.
Goal: Practice diagnosing the string-vs-Date comparison problem in a realistic symptom.
Challenge 3: Write a $jsonSchema Validator
Write a $jsonSchema validator for a products collection requiring a name (string) and a price (number, minimum 0).
Goal: Practice the validator syntax for required fields and value constraints.
Typing a decimal number in mongosh — { price: 19.99 } — stores it as a Double by default, ordinary binary floating-point. Floating-point can't represent every decimal value exactly, the same well-known issue behind 0.1 + 0.2 not exactly equaling 0.3 in most programming languages. For a display price this rarely matters, but for anything involving repeated financial arithmetic — running totals, tax calculations, currency conversion — those tiny errors can accumulate into a genuinely wrong result. Use NumberDecimal("19.99") to store an exact Decimal128 value instead whenever a field represents real money.
🎯 What's Next
The next chapter covers Indexes — single-field and compound indexes, how MongoDB actually chooses which index to use via explain(), and what a missing index really costs.