How it works
The explanation, with examples you can run.
Embedding
Embed when the child belongs to the parent, is read with it, and the number of children is small and bounded: order lines, addresses, a product's variants. You get one read, no join, and a single atomic update:
db.orders.updateOne( { _id: "o-1001", status: "pending" }, { $push: { items: { sku: "sku-33", qty: 1, priceCents: 400 } }, $inc: { totalCents: 400 } })The status check, the new line and the new total are applied together or not at all, with no transaction.
Referencing
Reference when the related data is shared by many parents (a product used in many orders), is large, changes on its own schedule, or grows without limit. Store the id, index it, and read the related documents in a second query or with $lookup:
db.comments.createIndex({ postId: 1, createdAt: -1 })db.comments.find({ postId: "p-17" }).sort({ createdAt: -1 }).limit(20)There are useful middle grounds. An extended reference copies a few stable fields (a customer's name) next to the id so most reads need no lookup. A subset keeps the latest few children embedded and the full history in its own collection. A bucket groups many small items, such as one document per sensor per hour, so the count per document stays bounded.
Unbounded arrays and the 16 MB limit
A BSON document can be at most 16 MiB. The limit is enforced by the server, so a document that grows past it through updates starts failing at an unpredictable moment. Here, a 9 MB string is copied into a second field:
db.big.insertOne({ _id: 1, blob: "x".repeat(9 * 1024 * 1024) })db.big.updateOne({ _id: 1 }, [{ $set: { copy: "$blob" } }])MongoServerError: Plan executor error during update :: caused by :: Serializing Document failed :: caused by :: Size 18874404 exceeds maximum 16793600(The maximum shown includes a little internal headroom over 16 MiB.) Most designs suffer long before the limit: every update rewrites and replicates a large document, and every read moves it over the network. For files, MongoDB offers GridFS, which splits content into chunk documents. For growing lists, move the children to their own collection.
A checklist
- List the operations with their rates, for example "show order: 2,000/s; add line: 50/s".
- For each relationship, estimate the worst-case count of children.
- Embed bounded, owned, read-together data; reference the rest.
- Add indexes for each query (next stage), and validation for the shape you settled on.
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.