MongoDBCollections Deep Dive

Collections

A collection is MongoDB's analog of a relational table: a named group of documents inside a database. Unlike a table, a collection does not enforce a schema by default — documents in the same collection can have different fields. This page covers creating, configuring, renaming, listing, and dropping collections.

Implicit Creation

Like databases, collections spring into existence on first use. Inserting into a collection that does not exist creates it automatically:

Implicit creation

JS
use shop
show collections               // (empty)

db.products.insertOne({ name: "Keyboard", price: 79.99 })

show collections
products

Creating an index on a non-existent collection also creates it. Implicit creation is convenient, but it means a typo (db.prodcuts.find()) silently queries an empty collection instead of erroring.

Explicit Creation with createCollection

db.createCollection() creates a collection up front. You only need it when you want to pass options — validators, capped sizing, collation, or time-series configuration.

createCollection

JS
// Plain explicit creation (rarely needed)
db.createCollection("orders")

// With options — this is why explicit creation exists
db.createCollection("orders", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["orderNumber", "total"],
      properties: {
        orderNumber: { bsonType: "int" },
        total: { bsonType: ["double", "int", "decimal"], minimum: 0 }
      }
    }
  },
  validationLevel: "strict",     // apply to all inserts and updates
  validationAction: "error"      // reject invalid documents (vs "warn")
})
Collection Options

Option

Purpose

validator

JSON Schema (or query expression) that documents must satisfy

validationLevel

'strict' (default) or 'moderate' (only validate documents that already pass)

validationAction

'error' (reject) or 'warn' (log and allow)

capped, size, max

Create a fixed-size capped collection

collation

Default string-comparison rules (locale, case sensitivity)

timeseries

Create a time-series collection (metaField, timeField, granularity)

expireAfterSeconds

TTL for time-series / clustered collections

Capped Collections

A capped collection has a fixed maximum size. Once full, the oldest documents are overwritten by new ones — a built-in circular buffer. Insertion order is preserved, making them ideal for logs and recent-activity feeds.

Creating and using a capped collection

JS
db.createCollection("recentEvents", {
  capped: true,
  size: 10 * 1024 * 1024,   // max bytes (required) — 10 MB
  max: 10000                // optional: max number of documents
})

db.recentEvents.insertOne({ type: "login", userId: 42, at: new Date() })

// Documents come back in insertion order (natural order)
db.recentEvents.find()

// Check whether a collection is capped
db.recentEvents.isCapped()   // true
Warning
Capped collections have hard restrictions: you cannot delete individual documents, updates must not grow a document, and you cannot shard them. For most expiry use cases, a normal collection with a TTL index is more flexible.
Default Collation

Collation controls how strings compare and sort. Setting it at creation time makes every query, sort, and index on the collection case-insensitive (or locale-aware) by default:

Case-insensitive collection

JS
db.createCollection("customers", {
  collation: { locale: "en", strength: 2 }   // strength 2 = ignore case
})

db.customers.insertOne({ name: "Alice" })

// Matches despite the different case — collection collation applies
db.customers.find({ name: "alice" })
Note
A collection's default collation is fixed at creation and cannot be changed later. To change it you must create a new collection and copy the data across.
Listing Collections

Listing and inspecting

JS
show collections                 // mongosh shorthand

db.getCollectionNames()          // array of names
db.getCollectionInfos()          // names + options + info
db.getCollectionInfos({ name: "orders" })  // filter to one collection

db.orders.stats()                // size, count, index details
db.orders.countDocuments()       // accurate document count
[
  {
    name: 'orders',
    type: 'collection',
    options: { validator: { ... }, validationLevel: 'strict' },
    info: { readOnly: false, uuid: UUID('8a1c...') },
    idIndex: { v: 2, key: { _id: 1 }, name: '_id_' }
  }
]
Renaming a Collection

renameCollection

JS
// Rename within the same database
db.orders.renameCollection("orders_archive")

// Overwrite an existing target collection
db.orders_new.renameCollection("orders", true)  // dropTarget = true
  • Renaming keeps all documents and indexes intact.

  • You cannot rename across databases with this helper (use $out in aggregation or dump/restore instead).

  • Renaming is not allowed on sharded collections in older versions; check your server version's documentation before relying on it.

Dropping a Collection

drop

JS
db.orders_archive.drop()
true

drop() removes the collection, all of its documents, and all of its indexes. Compare that with deleteMany({}), which removes documents but keeps the collection and its indexes:

Operation

Documents

Indexes

Collection options

Speed

db.coll.deleteMany({})

Removed

Kept

Kept

Slow (one-by-one)

db.coll.drop()

Removed

Removed

Removed

Fast (metadata operation)

Tip
To empty a large collection you plan to keep using, it is often faster to drop it and recreate the indexes than to run a huge deleteMany — but remember that drop also erases validators and collation options.
Collection Naming Rules
  • Names must not be empty, must not contain the null character, and must not start with system. (reserved prefix).

  • Avoid the dollar sign in names — it is reserved in most contexts.

  • Names including dots (orders.archive) are legal but confusing in shell helpers; prefer camelCase or snake_case instead.

  • The full namespace (database.collection) has a length limit (255 bytes in modern versions); keep names reasonably short.

  • Convention: plural nouns for entity collections — users, orders, products.

Accessing awkward names

JS
// If a name clashes with a shell property or contains odd characters:
db.getCollection("weird-name.v2").find()
db["weird-name.v2"].countDocuments()
Views

A view is a read-only, aggregation-defined collection. It stores no data; queries against it run the underlying pipeline on the fly:

Creating a view

JS
db.createView(
  "activeCustomers",       // view name
  "customers",             // source collection
  [ { $match: { status: "active" } },
    { $project: { name: 1, email: 1 } } ]
)

db.activeCustomers.find({ name: /^A/ })  // queries the view like a collection
Summary
  • Collections group documents inside a database and are created implicitly on first insert or index build.

  • Use db.createCollection() when you need options: validators, capped sizing, collation, or time-series settings.

  • Capped collections are fixed-size circular buffers with strict limitations — TTL indexes are usually the better expiry tool.

  • renameCollection preserves data and indexes; drop() removes everything including indexes and options.

  • Views expose an aggregation pipeline as a read-only collection.