How it works
The explanation, with examples you can run.
What a document looks like
db.orders.insertOne({ _id: "o-1001", customerId: 42, status: "pending", createdAt: new Date("2026-10-16T09:30:00Z"), totalCents: 2500, shipTo: { city: "Leeds", postcode: "LS1 4AP" }, items: [ { sku: "sku-7", qty: 2, priceCents: 750 }, { sku: "sku-19", qty: 1, priceCents: 1000 } ]})Every document needs a unique _id, and the collection always has an index on it. If you do not supply one, the driver generates an ObjectId: 12 bytes made of a 4-byte timestamp in seconds, a 5-byte random value per process and a 3-byte counter. ObjectIds therefore sort roughly by creation time, which is handy, and become a problem in the sharding topic.
Types matter
BSON distinguishes 32-bit int, 64-bit long, double and decimal, and mongosh picks for you. Checking with $type on the server:
db.t.insertOne({ _id: 1, a: 42, b: 4.5, c: 3000000000, d: NumberDecimal("19.99"), e: new Date(0) })db.t.aggregate([{ $project: { _id: 0, a: { $type: "$a" }, b: { $type: "$b" }, c: { $type: "$c" }, d: { $type: "$d" }, e: { $type: "$e" } } }])[ { "a": "int", "b": "double", "c": "double", "d": "decimal", "e": "date" } ]3000000000 silently became a double. Values of different numeric types still compare by value, but store dates as dates rather than strings, money as integer cents or Decimal128, and be consistent: an index on a field holding a mix of strings and numbers sorts them into separate groups.
When it fits, and when it does not
| Fits well | Fits poorly |
|---|---|
| Aggregates read and written as a unit (orders with lines, user profiles, content items) | Many-to-many data joined in different directions for ad-hoc reports |
| Varying attributes per record (catalogues, settings, event payloads) | Strict invariants spanning many records (double-entry ledgers) |
| Read-heavy pages that want one round trip | Data that is mostly shared and referenced, rarely owned |
None of these are absolute. MongoDB has $lookup for joins, multi-document transactions and schema validation, and PostgreSQL has jsonb columns. The question is which operations are common and cheap in your workload. A document database rewards designing around the queries you run (the next topic); a relational database rewards designing around the entities and letting the planner find a way to answer any question.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.