Databases and Collections Are Created Lazily
In SQL you must run CREATE DATABASE and CREATE TABLE before you can store anything. MongoDB works the other way round: it creates a database or a collection at the moment you first write data into it, and not a second earlier. The command use shopDB does not create shopDB; it only points the db variable at that name so your next command knows where to go.
This is convenient, and it has one sharp edge. Because a collection springs into existence on first use, a typo in a collection name never produces an error — it produces a brand-new, nearly empty collection. Write db.prodcuts.insertOne(...) and MongoDB will cheerfully create prodcuts next to products, and you will spend twenty minutes wondering why your query returns nothing. When a query surprises you, run show collections early.
use shopDB // just points db at shopDB; nothing created yet
show dbs // shopDB is NOT listed — it does not exist
db.products.insertOne({ name: "Notebook", price: 60 })
show dbs // now shopDB appears
show collections // products
// The typo trap
db.prodcuts.insertOne({ name: "Pen", price: 10 }) // no error!
show collections // prodcuts, products <- two collections db.getCollectionNames()returns the same list asshow collections, but as a real JavaScript array, so you can use it inside a script or a loop.
Naming Rules and Sensible Conventions
The hard rules are short. A database name must be fewer than 64 characters and cannot contain characters that mean something in a file path, such as /, \, ., ", $, *, <, >, :, | or ?. A collection name cannot be empty, cannot contain the $ character, and must not begin with system., which MongoDB reserves for its own internal use.
The soft rules matter more day to day, because MongoDB will not stop you from making a mess. Names are case-sensitive: Users and users are two different collections, and having both is a bug waiting to happen. Pick one convention and hold to it across the whole project.
- Name collections after what they hold, in the plural and in lower case:
users,orders,products - Use one word where you can; if you need two, pick a separator and never mix (
order_itemsororderItems, not both) - Keep field names short but readable — every field name is stored in full inside every single document, so
qtyin a million-document collection costs less space thanquantityOrderedByCustomer - Do not start a field name with
$, and avoid.inside field names, because both have special meaning in queries - Rename a collection with
db.oldName.renameCollection("newName")rather than copying documents by hand
Creating a Collection Explicitly — and Why You Would
Since collections appear on their own, db.createCollection() exists for one reason: to set options that must be decided before the first document arrives. If you have no options to set, calling it adds nothing.
The two options worth knowing now are validator, which makes the database itself reject documents that do not match a shape you describe, and capped, which gives you a fixed-size collection that discards its oldest documents automatically. Validation gets a full lesson later; the example below is only to show where the option lives.
// Plain creation — only useful with options attached
db.createCollection("orders")
// With a validator: the server refuses documents missing these fields
db.createCollection("customers", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "email"],
properties: {
name: { bsonType: "string", description: "required string" },
email: { bsonType: "string", pattern: "^.+@.+$" }
}
}
}
})
db.customers.insertOne({ name: "Ananya" })
// MongoServerError: Document failed validation Capped Collections: Fixed-Size Ring Buffers
A capped collection has a maximum size in bytes fixed at creation time. Once it is full, every new insert silently removes the oldest document to make room. That makes it a natural fit for rolling logs: the last few megabytes of activity are always there, and the collection can never grow and fill your disk.
The restrictions are real and worth remembering before you choose one. You cannot delete individual documents from a capped collection — deleteOne and deleteMany both fail on it, and the only way to clear it is drop(). You cannot change its size after creation either. Documents are kept in insertion order and cannot be reordered.
For the common case of "delete this data after some time", a capped collection is usually the wrong tool. What you actually want is a TTL index — an index with an expiry attached, which lets a background task delete documents once a date field is older than the limit you set. It works on an ordinary collection, so all the normal operations still work.
// Capped: at most 1 MB, and at most 1000 documents
db.createCollection("appLogs", {
capped: true,
size: 1048576, // bytes — required
max: 1000 // optional document-count ceiling
})
db.appLogs.deleteMany({})
// MongoServerError: cannot remove from a capped collection
// Usually a better answer: an ordinary collection + a TTL index.
// Documents disappear one hour after their createdAt value.
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 }) - TTL deletion is not instant. A background task sweeps expired documents roughly once a minute, so a document may survive a little past its expiry time. Never rely on a TTL index for anything that must vanish at an exact moment.
Dropping Things: the Commands With No Undo
Three commands destroy data, and none of them asks you to confirm. db.collection.drop() removes an entire collection along with all of its indexes. db.dropDatabase() removes the current database and everything in it. db.collection.deleteMany({}) empties a collection but keeps the collection and its indexes in place.
The dangerous one is db.dropDatabase(), because it acts on whatever db currently points at — and the shell gives no clue in its prompt about which database that is. If you switched to production twenty minutes ago and forgot, that command will do exactly what you told it to. Make it a habit to type db on its own first and read the answer before running anything destructive.
Choosing between drop() and deleteMany({}) comes down to indexes. Dropping is much faster because it discards the whole storage file at once, but you lose every index you created and must build them again. deleteMany({}) walks the collection removing documents one by one, which is slower on large data, but your indexes and any validator survive. For clearing test data between runs where the setup is scripted, drop. For emptying a live collection whose configuration you want to keep, delete.
db // ALWAYS check where you are first
// shopDB
db.products.drop() // collection gone, indexes gone
db.logs.deleteMany({}) // documents gone, collection and indexes stay
db.dropDatabase() // the entire current database, no confirmation
// Safer habit inside scripts: name the database explicitly
const target = db.getSiblingDB("shopDB_test")
target.dropDatabase() db.getSiblingDB("name")gives you a handle to another database without switchingdb. Using it in scripts means the script cannot accidentally act on whichever database you happened to be sitting in.
Counting and Inspecting
Two methods count documents, and they answer slightly different questions. countDocuments(filter) genuinely inspects the data and gives an exact answer, which is what you want for anything a user will see. estimatedDocumentCount() reads a number the storage engine keeps in metadata, so it returns instantly no matter how large the collection is — but it accepts no filter and can be briefly out of date after a crash or during a shard migration.
The rule is simple: if the number appears in your application's output, use countDocuments. If you only want a rough sense of how big a collection is while exploring in the shell, estimatedDocumentCount is fine and much cheaper. The old count() method that mixed the two behaviours has been removed from modern drivers, so do not reach for it.
db.products.countDocuments() // exact, scans
db.products.countDocuments({ price: { $gt: 500 } }) // exact, with a filter
db.products.estimatedDocumentCount() // instant, whole collection only
db.stats() // size, storage, index count for the database
db.getCollectionNames() // [ 'products', 'orders', 'users' ]
db.products.getIndexes() // what indexes exist on this collection 