Documents, collections and namespaces
The rules every document follows: _id, field names, size limits, nesting, and how collections and databases are created.
- Know the constraints on documents (16 MB, _id, field names)
- Understand implicit creation of databases and collections
- Use dot notation to reach nested fields
- Explain what a namespace is
conceptThe concept
- _id: required, unique per collection, immutable, can be any type except an array. Default is an ObjectId.
- Size: a document can be at most 16 MB of BSON. Nesting depth is limited to 100 levels.
- Field names: case-sensitive strings. Avoid names that start with
$or contain.; they are legal since 5.0 but awkward to query. - Field order is preserved;
_idis always first. - Namespace:
database.collection, for exampleshop.orders. Indexes, the oplog and the profiler all refer to namespaces.
Databases and collections are created lazily: the first insert into db.newThing creates both. Use db.createCollection() when you need options such as a validator, capped size, time series or clustered index.
whyWhy it matters
The 16 MB limit and the shape of your documents drive schema design. Arrays that grow without bound (every click a user ever made) eventually hit the limit and make every read and write of that document expensive. Knowing the rules early prevents designs you would have to migrate later.
Use caseReal-world scenario
A customer document embeds the current address (bounded, read with the customer), but the list of orders lives in its own collection referencing customerId, because a loyal customer could place thousands of orders.
Try itExamples
Run and edit these in the interactive lesson.
db.customers.find({ "address.city": "Pune" }, { name: 1, address: 1 }).limit(3)
db.customers.countDocuments({ "address.country": "India" })use lab
db.notes.insertOne({ text: "first note", tags: ["intro"] })
show collections
db.notes.find()
use shopuse lab
db.audit.drop()
db.createCollection("audit", { capped: true, size: 1048576, max: 3 })
for (let i = 1; i <= 5; i++) db.audit.insertOne({ seq: i })
db.audit.find({}, { _id: 0 })
use shopinternalsUnder the hood
Each collection is a WiredTiger table keyed by an internal RecordId; the _id index maps your _id values to RecordIds. Since 5.3 you can create clustered collections where documents are stored in _id order and the separate _id index disappears, which helps time-ordered data.
Avoid / preferDo this, not that
db.metrics.insertOne({ "cpu.load": 0.7, "$price": 10 })db.metrics.insertOne({ cpu: { load: 0.7 }, price: 10 })Since 5.0 field names may contain dots and dollar signs, but the query language reads dots as paths and dollars as operators. Reaching those fields needs $getField and $setField; ordinary names avoid the trouble.
db.users.updateOne({ _id: 1 },
{ $push: { logins: new Date() } }) // foreverdb.users.updateOne({ _id: 1 },
{ $push: { logins: { $each: [new Date()], $slice: -50 } } })An array that grows forever eventually hits the 16 MB document limit, and every update rewrites an ever larger document. Cap it with $slice, or move history into its own collection.
PitfallsCommon mistakes
- Storing unbounded arrays (comments, events, logs) inside one document.
- Using values such as user input as field names (
{ "user@example.com": ... }). Field names should be static; put variable data in values. - Typos in collection names silently create new collections.
db.order.insertOne()writes toorder, notorders.
LimitsWhen not to use it
Do not use one document as a container for a whole tenant or a whole day of events, and do not create one collection per customer or per day by the thousands. Every collection and index is a separate WiredTiger file; tens of thousands of them slow startup, checkpoints and memory use. Use a field such as tenantId instead.
SecuritySecurity notes
Values of _id and unique-index fields are echoed in duplicate-key errors and in slow-query logs. Avoid using emails, phone numbers or national ID numbers as _id; use an opaque id and keep personal data in ordinary fields. MongoDB Enterprise can also redact client data from logs with redactClientLogData.
ChallengeTry it yourself
Find the three largest order documents by BSON size, showing orderNo, the number of line items and the size in bytes.
Show one solution
db.orders.aggregate([
{ $project: { orderNo: 1, items: { $size: "$items" }, bytes: { $bsonSize: "$$ROOT" } } },
{ $sort: { bytes: -1 } },
{ $limit: 3 }
])InterviewQuestions you should be able to answer
What happens if two documents in a collection have different fields?
Nothing breaks; collections do not enforce a structure unless you add a validator. Queries simply do not match documents missing a field (unless you query for missing values). In practice you keep shapes consistent per type and use a $jsonSchema validator for critical fields.
How do you handle data that might exceed 16 MB?
Do not let a single document grow without bound: move the growing part into its own collection (one document per item or per bucket of items), use the bucket pattern, or store large binary content with GridFS or object storage and keep a reference.
QuizCheck your understanding
4 questions with instant feedback in the interactive lesson; a wrong answer goes into your review deck.