MongoDB Cheatsheet
mongosh Basics
Use this MongoDB reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Connecting
mongosh is the shell. The legacy mongo shell was removed in MongoDB 6.0, so any
snippet that starts with mongo is out of date. Everything below targets 8.0.
mongosh # localhost:27017, "test" db mongosh "mongodb://localhost:27017/shop" mongosh "mongodb://app:secret@db1:27017/shop?authSource=admin" mongosh "mongodb+srv://app@cluster0.ab12c.mongodb.net/shop" # Atlas mongosh --host db1 --port 27017 -u app -p --authenticationDatabase admin mongosh "mongodb://a:27017,b:27017,c:27017/shop?replicaSet=rs0"
| URI fragment | What it does |
|---|---|
mongodb:// | Explicit host list, you name every node yourself |
mongodb+srv:// | DNS seed list, one hostname resolves the whole cluster. Implies tls=true |
?authSource=admin | Database where the user is defined, not the data db. Missing it is the single most common auth failure |
?replicaSet=rs0 | Lets the driver find the primary. Required for transactions and change streams |
?retryWrites=true | Retry a failed write once. Default true on modern drivers |
?readPreference=secondaryPreferred | Send reads to secondaries |
-p with no value | Prompt for the password instead of leaving it in shell history |
--norc | Skip ~/.mongoshrc.js |
--nodb | Start the shell without connecting to anything |
Getting around
use switches database and does not require it to exist. A database and a collection
spring into existence on the first write, never on use alone.
| Command | Result |
|---|---|
show dbs | Databases with size on disk. A database holding no data is not listed |
use shop | Switch. Sets the db global |
db | Print the current database name |
show collections | Collections and views here |
db.getCollectionNames() | The same list as a JS array you can loop over |
db.getSiblingDB("logs") | Reach another database without leaving this one |
db.help() | Database-level methods |
db.orders.help() | Collection-level methods |
db.orders. then Tab | Complete method names, and field names in recent versions |
show users · show roles | Auth objects in the current db |
exit or Ctrl-D | Leave |
use shop db.orders.countDocuments() db.getSiblingDB("logs").events.findOne() db.adminCommand({ listDatabases: 1 })
BSON types you will actually type
The shell is JavaScript, so every bare number is a double. When the type matters you have to say so, and for money it always matters.
| Constructor | BSON type | Notes |
|---|---|---|
ObjectId() | ObjectId | 12 bytes. The first 4 are a Unix timestamp, so ids sort roughly by creation time |
ObjectId("65f...").getTimestamp() | Date | A free created_at, no extra field needed |
ISODate("2026-01-15T09:30:00Z") | Date | Milliseconds since epoch, stored UTC |
new Date() | Date | Same BSON type as ISODate |
UUID() | Binary, subtype 4 | For ids you want generated client side |
NumberInt(7) | int32 | |
NumberLong("9007199254740993") | int64 | Past 2⁵³ a double silently loses digits, so pass a string |
NumberDecimal("19.99") | decimal128 | 34 significant digits, exact. The right type for money |
BinData(0, "<base64>") | Binary | |
/^ac/i | Regex | A native JS regex works as a query value |
Timestamp(1, 1) | Timestamp | Internal replication type, not a user-facing date |
MinKey · MaxKey | sentinel | Sort below and above every other value |
// Wrong: binary floating point cannot represent most decimals db.orders.insertOne({ total: 0.1 + 0.2 }) // stores 0.30000000000000004 // Right: decimal128, or plain integer minor units db.orders.insertOne({ total: NumberDecimal("19.99") }) db.orders.insertOne({ total_cents: NumberInt(1999) }) ObjectId("65f1a2b3c4d5e6f701234567").getTimestamp() // 2024-03-13T...
Reading output
A find() returns a cursor. mongosh prints the first 20 documents and then waits,
and it pulls the next batch. Output is already pretty-printed, so .pretty() is a
leftover from the old shell and buys you nothing.
db.orders.find() // prints 20, then: Type "it" for more it // next batch printjson(db.orders.findOne()) JSON.stringify(db.orders.findOne()) // BSON types collapse, ObjectId becomes {} EJSON.stringify(db.orders.findOne()) // keeps them: {"$oid":"65f1..."} EJSON.parse('{"_id": {"$oid": "65f1a2b3c4d5e6f701234567"}}') config.set("displayBatchSize", 50) // print 50 per batch instead of 20 config.set("inspectDepth", 8) // stop deep documents printing as [Object]
Extended JSON is the wire-safe text form of BSON. Use EJSON any time output leaves
the shell, because plain JSON.stringify destroys dates, decimals, and object ids.
Both query shapes take explain, and the noun order differs between them:
db.orders.find({ status: "paid" }).explain("executionStats") db.orders.explain("executionStats").aggregate([{ $match: { status: "paid" } }])
Scripting
--eval for one expression, --file for a script, and load() from inside an
interactive session. Both flags are repeatable and both exit when finished.
| Flag | Does |
|---|---|
--eval '<js>' | Run an expression, print the result, exit |
--file s.js / -f s.js | Run a script file and exit |
--quiet | Drop the banner and connection chatter. Required for clean piping |
--json / --json=canonical | EJSON on stdout, ready for jq |
--eval + --file together | Files run first, then the eval expressions |
mongosh "$URI" --quiet --eval 'db.orders.countDocuments({status:"paid"})' mongosh "$URI" --quiet --json --eval 'db.orders.find().limit(5).toArray()' | jq . mongosh "$URI" --file migrate-2026-01.js
// Inside the shell. Path is relative to the shell's working directory. load("./seed.js") // ~/.mongoshrc.js runs on every interactive start config.set("displayBatchSize", 50); prompt = () => `${db.getName()}> `;
Scripts are plain JavaScript, so
await,for...of, andtry/catchall work. Top-levelawaitis supported, which matters because most driver methods return promises thatmongoshnormally awaits for you.