The MongoDB Shell — mongosh Fundamentals

The MongoDB Shell (mongosh) is your direct command-line interface to any MongoDB database — local or Atlas. Every developer who works seriously with MongoDB uses it to inspect data, test queries before putting them in application code, debug problems, run administrative tasks, and verify that operations like indexes and aggregations work as expected. In this lesson you will install mongosh, connect to both a local MongoDB instance and an Atlas cluster, navigate databases and collections, and run all the essential operations you will use throughout the MERN series.

Connecting to MongoDB

# ── Connect to local MongoDB (default) ───────────────────────────────────────
mongosh
# Connects to mongodb://127.0.0.1:27017 by default
# Output: Connecting to: mongodb://127.0.0.1:27017/
#         Current Mongosh Log ID: ...
#         Using MongoDB: 7.x.x
#         Using Mongosh: 2.x.x

# ── Connect to a specific local database directly ─────────────────────────────
mongosh mongodb://localhost:27017/blogdb

# ── Connect to MongoDB Atlas ──────────────────────────────────────────────────
mongosh "mongodb+srv://mern_user:password@cluster0.xxxxx.mongodb.net/blogdb"

# ── Connect with authentication ───────────────────────────────────────────────
mongosh --host localhost --port 27017 --username admin --password secret

# ── Exit the shell ────────────────────────────────────────────────────────────
exit    # or Ctrl+D
Note: mongosh (the modern MongoDB Shell) replaces the older mongo shell. If you run mongo on a modern MongoDB installation, you will get a deprecation warning. Always use mongosh. It has better JavaScript support, autocomplete, syntax highlighting, and built-in help. Install it separately from MongoDB if needed: npm install -g mongosh or download it from mongodb.com/tools/mongosh.
Tip: mongosh is a full JavaScript REPL — you can write regular JavaScript inside it. This means you can use variables, loops, functions, and Date objects in your shell commands. For example: const postId = db.posts.findOne().id; db.comments.find({ postId }). You can also load and run a JavaScript file directly: mongosh --file seed.js.
Warning: db.collection.drop() and db.dropDatabase() are irreversible — they permanently delete all data with no confirmation prompt. Never run these commands on a production database without a verified backup. In production, use role-based access control (RBAC) to ensure that application database users cannot drop collections or databases.

Navigating Databases and Collections

# ── Database navigation ────────────────────────────────────────────────────────
show dbs                   # list all databases and their sizes
use blogdb                 # switch to the blogdb database (creates it on first insert)
db                         # print the name of the current database

# ── Collection navigation ─────────────────────────────────────────────────────
show collections           # list all collections in the current database
db.posts.stats()           # collection statistics (document count, storage size, indexes)
db.posts.countDocuments()  # count all documents in the collection
db.posts.countDocuments({ published: true })  # count with a filter

Reading Documents

# ── findOne — return the first matching document ─────────────────────────────
db.posts.findOne()                          # first document in the collection
db.posts.findOne({ published: true })       # first published post
db.posts.findOne({ slug: "getting-started" }) # by slug

# ── find — return all matching documents ─────────────────────────────────────
db.posts.find()                             # all documents (returns a cursor)
db.posts.find({ published: true })          # all published posts
db.posts.find({ tags: "mern" })             # posts with 'mern' in the tags array

# ── Projection — return only specific fields ──────────────────────────────────
db.posts.find({}, { title: 1, slug: 1, _id: 0 })
# { title: "...", slug: "..." }  (1 = include, 0 = exclude, _id excluded by default if 0)

# ── Sorting, limiting, skipping ───────────────────────────────────────────────
db.posts.find({ published: true })
  .sort({ createdAt: -1 })   # -1 = descending (newest first)
  .limit(5)                  # maximum 5 documents
  .skip(10)                  # skip first 10 (for pagination: page 3 = skip 20, limit 10)

# ── pretty() — format output for readability ──────────────────────────────────
db.posts.findOne().title     # access a specific field from the result
db.posts.find().toArray()    # convert cursor to a JavaScript array

Writing Documents

# ── insertOne — insert a single document ─────────────────────────────────────
db.posts.insertOne({
  title:     "My First Post",
  body:      "Hello from mongosh!",
  tags:      ["test", "mongosh"],
  published: false,
  createdAt: new Date()
})
# Returns: { acknowledged: true, insertedId: ObjectId("...") }

# ── insertMany — insert multiple documents ────────────────────────────────────
db.posts.insertMany([
  { title: "Post One", body: "Content 1", published: true,  createdAt: new Date() },
  { title: "Post Two", body: "Content 2", published: false, createdAt: new Date() }
])
# Returns: { acknowledged: true, insertedIds: { '0': ObjectId, '1': ObjectId } }

Updating Documents

# ── updateOne — update the first matching document ───────────────────────────
db.posts.updateOne(
  { title: "My First Post" },          // filter
  { $set: { published: true } }        // update operator
)

# ── updateMany — update all matching documents ────────────────────────────────
db.posts.updateMany(
  { published: false },
  { $set: { published: true, updatedAt: new Date() } }
)

# ── Common update operators ───────────────────────────────────────────────────
db.posts.updateOne({ _id: id }, { $set:       { title: "New Title" } })       # set a field
db.posts.updateOne({ _id: id }, { $unset:     { excerpt: "" } })               # remove a field
db.posts.updateOne({ _id: id }, { $inc:       { viewCount: 1 } })              # increment
db.posts.updateOne({ _id: id }, { $push:      { tags: "mongodb" } })          # add to array
db.posts.updateOne({ _id: id }, { $pull:      { tags: "test" } })             # remove from array
db.posts.updateOne({ _id: id }, { $addToSet:  { tags: "unique-tag" } })       # add if not exists

Deleting Documents

# ── deleteOne — delete the first matching document ───────────────────────────
db.posts.deleteOne({ title: "My First Post" })
# Returns: { acknowledged: true, deletedCount: 1 }

# ── deleteMany — delete all matching documents ────────────────────────────────
db.posts.deleteMany({ published: false })   # delete all drafts

# ── findOneAndDelete — delete and return the deleted document ─────────────────
db.posts.findOneAndDelete({ slug: "test-post" })
# Returns the document before deletion — useful for confirmation

Useful Shell Utilities

# ── Get help ──────────────────────────────────────────────────────────────────
help                        # top-level help
db.help()                   # database-level help
db.posts.help()             # collection-level help

# ── Format and inspect ────────────────────────────────────────────────────────
db.posts.find().count()                    # total count of cursor results
db.posts.distinct("tags")                  # all unique tag values
db.posts.find({ published: true }).explain("executionStats") # query plan + performance

# ── Index information ─────────────────────────────────────────────────────────
db.posts.getIndexes()                      # list all indexes
db.posts.createIndex({ slug: 1 }, { unique: true })  # create an index
db.posts.dropIndex("slug_1")               # drop an index by name

Common Mistakes

Mistake 1 — Using find() without limit() on large collections

❌ Wrong — fetching all documents at once in the shell:

db.posts.find()   # returns ALL documents — could be millions, locks terminal

✅ Correct — always limit shell queries to a reasonable number:

db.posts.find().limit(10)   # ✓ safe to run on any size collection

Mistake 2 — Accidentally running drop on the wrong database

❌ Wrong — running dropDatabase() without checking which database is active:

db.dropDatabase()   # drops the CURRENT database — no confirmation!

✅ Correct — always check the current database before any destructive operation:

db   # confirm current database name first
use blogdb
db   # confirm again
db.dropDatabase()   # only then proceed

Mistake 3 — Querying without the dollar sign on update operators

❌ Wrong — missing the $ prefix on update operators:

db.posts.updateOne({ _id: id }, { set: { published: true } })
# 'set' without $ replaces the ENTIRE document with { set: { published: true } }

✅ Correct — all MongoDB update operators require the $ prefix:

db.posts.updateOne({ _id: id }, { $set: { published: true } })  # ✓

Quick Reference

Operation mongosh Command
Find one db.posts.findOne({ filter })
Find many db.posts.find({ filter }).sort().limit()
Insert one db.posts.insertOne({ doc })
Insert many db.posts.insertMany([{ doc1 }, { doc2 }])
Update fields db.posts.updateOne({ filter }, { $set: { field: val } })
Increment field db.posts.updateOne({ filter }, { $inc: { field: 1 } })
Delete one db.posts.deleteOne({ filter })
Delete many db.posts.deleteMany({ filter })
Count db.posts.countDocuments({ filter })
List indexes db.posts.getIndexes()

🧠 Test Yourself

In mongosh, you run db.posts.updateOne({ title: "Test" }, { published: true }). Instead of setting published to true, the entire document is replaced with { published: true } and all other fields are lost. What caused this?