Skip to content
mongosh Commands: MongoDB Shell Cheat Sheet

Click to use (opens in a new tab)

mongosh Commands: MongoDB Shell Cheat Sheet

September 23, 2026 by Chat2DBChat2DB Team

mongosh is the current MongoDB Shell. It replaced the legacy mongo shell, which was deprecated in MongoDB 5.0 and removed from server packages in 6.0. It is a full Node.js-based REPL, so you get syntax highlighting, autocompletion, modern JavaScript, and access to Node modules, while the familiar db.collection.method() style still works.

This cheat sheet collects the mongosh commands you will reach for day to day: connecting, navigating databases, CRUD, aggregation, indexes and explain, users and roles, administration, backups, and customizing the shell. Every example runs as written against a scratch database, so feel free to copy and paste.

Connecting with mongosh

Start the shell against a local server on the default port:

mongosh

Connect with a full connection string. Quote it so your shell does not interpret &:

mongosh "mongodb://localhost:27017/shop"
mongosh "mongodb+srv://cluster0.abcde.mongodb.net/shop" --username alice

When you pass --username without --password, mongosh prompts for the password, which keeps it out of your shell history. Other useful flags:

mongosh --host db1.example.com --port 27017 --authenticationDatabase admin -u alice
mongosh "mongodb://localhost:27017/shop" --quiet --eval 'db.orders.countDocuments()'
mongosh "mongodb://localhost:27017/shop" --file ./report.js

--eval runs a snippet and exits, which is handy in shell scripts. --quiet suppresses the startup banner. --file runs a script file.

Inside the shell, you can switch connections without restarting:

const other = connect("mongodb://localhost:27018/analytics")
other.events.countDocuments()

If connection strings are giving you trouble, see our guide to MongoDB connection strings or use the free MongoDB Connection String Builder (opens in a new tab).

Navigating databases and collections

show dbs                  // list databases (with size)
use shop                  // switch to (or lazily create) the "shop" database
db                        // print the current database name
show collections          // list collections in the current database
show users                // users defined in the current database
show roles                // roles in the current database
db.getCollectionNames()   // same as show collections, but returns an array
db.getSiblingDB("admin")  // reference another database without switching

use shop does not create anything on disk. The database appears in show dbs only after you write the first document into it.

To create a collection explicitly, for example with validation or as a capped collection:

db.createCollection("logs", { capped: true, size: 10 * 1024 * 1024 })

Drop things carefully:

db.logs.drop()        // drop a collection
db.dropDatabase()     // drop the current database

Inserting documents

db.products.insertOne({
  sku: "KB-001",
  name: "Mechanical Keyboard",
  price: 89.99,
  tags: ["peripherals", "keyboards"],
  stock: 120,
  createdAt: new Date()
})
 
db.products.insertMany([
  { sku: "MS-002", name: "Wireless Mouse", price: 29.5, tags: ["peripherals"], stock: 300, createdAt: new Date() },
  { sku: "MN-003", name: "27in Monitor", price: 249, tags: ["displays"], stock: 40, createdAt: new Date() },
  { sku: "CB-004", name: "USB-C Cable", price: 9.99, tags: ["cables"], stock: 0, createdAt: new Date() }
])

insertMany is ordered by default: it stops at the first error. Pass { ordered: false } to continue inserting the remaining documents after a failure such as a duplicate key.

If you do not provide _id, MongoDB generates an ObjectId. You can construct your own with ObjectId() and read its embedded timestamp with ObjectId("...").getTimestamp().

Querying with find

db.products.find()                               // all documents
db.products.findOne({ sku: "KB-001" })           // first match or null
db.products.find({ price: { $lt: 50 } })         // comparison operators
db.products.find({ tags: "peripherals" })        // matches an array element
db.products.find({ stock: { $gt: 0 }, price: { $gte: 20, $lte: 100 } })  // implicit AND
db.products.find({ $or: [{ stock: 0 }, { price: { $gt: 200 } }] })
db.products.find({ name: /monitor/i })           // regex, case-insensitive
db.products.find({ discount: { $exists: false } })

Projection, sort, limit, and skip

Projection is the second argument to find. 1 includes a field, 0 excludes it, and _id is included unless you exclude it:

db.products.find(
  { stock: { $gt: 0 } },
  { _id: 0, sku: 1, name: 1, price: 1 }
).sort({ price: -1 }).limit(5)

Pagination with skip works, but gets slower as the offset grows because the server still walks the skipped documents:

db.products.find().sort({ createdAt: -1 }).skip(20).limit(10)

For deep pagination, prefer a range query on an indexed field, such as find({ createdAt: { $lt: lastSeenCreatedAt } }).sort({ createdAt: -1 }).limit(10).

Counting and distinct values

db.products.countDocuments({ stock: 0 })   // accurate count with a filter
db.products.estimatedDocumentCount()       // fast, uses collection metadata
db.products.distinct("tags")

When a query returns more than one batch of results, mongosh prints the first batch and you type it to see the next.

Updating documents

// Set fields
db.products.updateOne(
  { sku: "KB-001" },
  { $set: { price: 79.99, updatedAt: new Date() } }
)
 
// Increment a counter
db.products.updateOne({ sku: "MS-002" }, { $inc: { stock: -1 } })
 
// Add to an array without duplicates, remove from an array
db.products.updateOne({ sku: "KB-001" }, { $addToSet: { tags: "sale" } })
db.products.updateOne({ sku: "KB-001" }, { $pull: { tags: "sale" } })
 
// Remove a field
db.products.updateMany({}, { $unset: { discount: "" } })
 
// Update many
db.products.updateMany({ stock: 0 }, { $set: { status: "out_of_stock" } })

Upsert

An upsert updates the matching document or inserts a new one if nothing matches. $setOnInsert sets fields only when an insert happens:

db.products.updateOne(
  { sku: "HD-005" },
  {
    $set: { name: "USB Headset", price: 39 },
    $setOnInsert: { createdAt: new Date(), stock: 0 }
  },
  { upsert: true }
)

The result object tells you what happened: matchedCount, modifiedCount, and upsertedId.

To read and modify atomically in one step, use findOneAndUpdate, which returns the document before the change by default, or after it with { returnDocument: "after" }.

replaceOne swaps the whole document (except _id) instead of modifying specific fields, so use it only when you really mean to overwrite everything.

Deleting documents

db.products.deleteOne({ sku: "CB-004" })
db.products.deleteMany({ status: "out_of_stock" })
db.products.deleteMany({})   // removes every document but keeps the collection and indexes

A good habit before any deleteMany is to run the same filter with countDocuments first and check that the number makes sense.

Aggregation pipeline examples

The aggregation pipeline processes documents through a sequence of stages. Some sample order data:

db.orders.insertMany([
  { customer: "c1", status: "paid",    total: 120, items: [{ sku: "KB-001", qty: 1 }], createdAt: ISODate("2026-09-01T10:00:00Z") },
  { customer: "c2", status: "paid",    total: 59,  items: [{ sku: "MS-002", qty: 2 }], createdAt: ISODate("2026-09-02T12:30:00Z") },
  { customer: "c1", status: "pending", total: 249, items: [{ sku: "MN-003", qty: 1 }], createdAt: ISODate("2026-09-03T08:15:00Z") },
  { customer: "c3", status: "paid",    total: 30,  items: [{ sku: "CB-004", qty: 3 }], createdAt: ISODate("2026-09-03T09:45:00Z") }
])

Revenue per customer for paid orders, highest first:

db.orders.aggregate([
  { $match: { status: "paid" } },
  { $group: { _id: "$customer", revenue: { $sum: "$total" }, orders: { $sum: 1 } } },
  { $sort: { revenue: -1 } }
])

Daily totals:

db.orders.aggregate([
  { $group: {
      _id: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } },
      total: { $sum: "$total" }
  } },
  { $sort: { _id: 1 } }
])

Units sold per SKU, unwinding the items array:

db.orders.aggregate([
  { $unwind: "$items" },
  { $group: { _id: "$items.sku", units: { $sum: "$items.qty" } } },
  { $sort: { units: -1 } }
])

Joining orders to products with $lookup:

db.orders.aggregate([
  { $unwind: "$items" },
  { $lookup: {
      from: "products",
      localField: "items.sku",
      foreignField: "sku",
      as: "product"
  } },
  { $unwind: "$product" },
  { $project: { _id: 0, customer: 1, sku: "$items.sku", name: "$product.name", qty: "$items.qty" } }
])

Put $match as early as possible so the pipeline can use indexes and processes fewer documents. $out and $merge stages write results to a collection when you want to materialize them.

Indexes and explain

db.products.createIndex({ sku: 1 }, { unique: true })
db.orders.createIndex({ customer: 1, createdAt: -1 })        // compound
db.products.createIndex({ name: "text" })                     // text search
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })  // TTL
db.orders.createIndex(
  { status: 1 },
  { partialFilterExpression: { status: "pending" } }          // partial
)
 
db.orders.getIndexes()
db.orders.dropIndex("status_1")

For compound indexes, a useful rule of thumb is equality fields first, then sort fields, then range fields.

Reading explain output

db.orders.find({ customer: "c1" }).sort({ createdAt: -1 }).explain("executionStats")

In the result, look at:

  • queryPlanner.winningPlan — IXSCAN means an index was used; COLLSCAN means a full collection scan.
  • executionStats.nReturned — documents returned.
  • executionStats.totalKeysExamined and totalDocsExamined — work done. Ideally these are close to nReturned.
  • executionStats.executionTimeMillis — elapsed time on the server.
  • A SORT stage means an in-memory sort; an index that matches the sort order avoids it.

Aggregations support explain too: db.orders.explain("executionStats").aggregate([...]).

Users and roles

Users are created in a specific database, usually admin, which becomes their authentication database:

use admin
db.createUser({
  user: "shop_app",
  pwd: passwordPrompt(),   // prompts instead of putting the password in history
  roles: [
    { role: "readWrite", db: "shop" },
    { role: "read", db: "analytics" }
  ]
})

Common built-in roles: read, readWrite, dbAdmin, userAdmin, clusterMonitor, backup, restore, readAnyDatabase, and root.

Manage users:

db.getUsers()
db.getUser("shop_app")
db.grantRolesToUser("shop_app", [{ role: "dbAdmin", db: "shop" }])
db.revokeRolesFromUser("shop_app", [{ role: "dbAdmin", db: "shop" }])
db.changeUserPassword("shop_app", passwordPrompt())
db.dropUser("shop_app")

Custom roles narrow privileges to specific actions:

db.createRole({
  role: "orderReader",
  privileges: [
    { resource: { db: "shop", collection: "orders" }, actions: ["find"] }
  ],
  roles: []
})

Check who you are authenticated as with db.runCommand({ connectionStatus: 1 }).

Administration commands

db.stats()                       // size, object count, indexes for current db
db.orders.stats()                // collection stats (newer servers favor $collStats)
db.serverStatus()                // server-wide metrics
db.version()                     // server version
db.hello()                       // topology: primary or secondary, set members
db.adminCommand({ listDatabases: 1 })
db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })

Finding and killing slow operations

// Operations running longer than 5 seconds
db.currentOp({ active: true, secs_running: { $gte: 5 } })
 
// Kill one by its opid
db.killOp(12345)

Before killing anything, check the op, ns, and command fields to make sure you are not interrupting an important internal or replication operation.

The profiler records slow operations in system.profile:

db.setProfilingLevel(1, { slowms: 100 })
db.system.profile.find().sort({ ts: -1 }).limit(5)

Replica set commands

rs.status()                 // member states, health, replication lag info
rs.conf()                   // current configuration
rs.printSecondaryReplicationInfo()
rs.stepDown(60)             // ask the primary to step down (run on primary)

For sharded clusters, sh.status() summarizes shards, databases, and chunk distribution.

Export, import, and backups

These are separate command-line tools from the MongoDB Database Tools package, not mongosh commands, so run them from your terminal:

# JSON or CSV export of one collection
mongoexport --uri="mongodb://localhost:27017/shop" --collection=products --out=products.json
mongoexport --uri="mongodb://localhost:27017/shop" --collection=products --type=csv --fields=sku,name,price --out=products.csv
 
# Import
mongoimport --uri="mongodb://localhost:27017/shop" --collection=products --file=products.json
 
# Binary backup and restore (preserves BSON types and indexes)
mongodump --uri="mongodb://localhost:27017/shop" --out=./backup
mongorestore --uri="mongodb://localhost:27017" ./backup

Use mongodump and mongorestore for backups; mongoexport produces JSON or CSV that is handy for moving data into other tools but does not preserve every BSON type exactly.

Customizing mongosh with .mongoshrc.js

mongosh runs ~/.mongoshrc.js at startup (unless you pass --norc). It is a good place for helpers and prompt tweaks:

// ~/.mongoshrc.js
prompt = () => `${db.getName()}> `;
 
// Quick helper: show the 5 most recent documents in a collection
globalThis.latest = (coll, n = 5) =>
  db.getCollection(coll).find().sort({ _id: -1 }).limit(n);

After restarting the shell, latest("orders") prints the newest orders.

The config API controls shell settings and persists them between sessions:

config.set("displayBatchSize", 50)   // documents per batch before typing "it"
config.set("editor", "vim")          // editor used by the edit command
config.get("displayBatchSize")

You can also load a script into the current session with load("./helpers.js").

Where a GUI helps

The shell is unbeatable for quick checks and scripting, but browsing unfamiliar collections, comparing documents side by side, or building a long aggregation pipeline is often faster with a visual tool. Chat2DB (opens in a new tab) connects to MongoDB alongside SQL databases and includes an AI assistant that can help draft queries, which you can then paste back into mongosh.

Keep this page open next to your terminal, and the everyday mongosh commands will quickly become muscle memory.