Reference

MongoDB & Redis Command Reference

Every command used across Lectures 11–20, with the dataset they were taught on. Search it during the lab instead of scrolling the notes.

🗄 Database: bdma 📦 Collection: rides 📄 Documents: 15,000 ⌨️ Shells: mongosh · redis-cli
—

Multi-word search is an AND. Typing index sort shows only rows containing both. Press Esc to clear.

Connecting & Loading Data

mongosh "mongodb+srv://<user>:<pass>@cluster0.xxxxx.mongodb.net/"
Connect to an Atlas cluster. The connection string comes from Atlas → Connect → Shell. Never commit it to git.
mongoimport --uri "<connection-string>" --db bdma --collection rides --file data/uber_bookings.json --jsonArray --drop
Load the course dataset. --jsonArray is required because the file is one JSON array; --drop makes the import repeatable.
use bdma
Switch the active database. MongoDB creates it lazily on first write — a typo here silently makes a new empty database.
show dbs show collections
List databases, then collections in the current database. First thing to run when an import looks like it did nothing.
db.rides.countDocuments()
Exact document count. Should return 15000 after a clean import. Prefer this over the faster-but-approximate estimatedDocumentCount() when verifying a load.
db.rides.findOne()
Return a single document. The fastest way to confirm the field names and nesting actually match what you expected.
db.rides.drop()
Delete the whole collection, indexes included. Reload with mongoimport afterwards.

Reading — find, project, sort, paginate

db.rides.find({ status: "Completed" })
Equality match. Returns a cursor; mongosh prints the first 20 and waits for it.
db.rides.find({ status: "Completed" }, { booking_id: 1, booking_value: 1, _id: 0 })
Projection. Second argument picks fields. _id is returned unless you explicitly suppress it.
db.rides.find({ "route.pickup": "Connaught Place" })
Query a nested field with dot notation. The quotes around the key are mandatory.
db.rides.find({ booking_value: { $gt: 500 } })
Comparison operators: $gt $gte $lt $lte $ne. Documents missing booking_value never match a $gt.
db.rides.find({ vehicle_type: { $in: ["Uber XL", "Premier Sedan"] } })
Match any value in a list. $nin is the negation.
db.rides.find({ status: "Completed", booking_value: { $gt: 800 } })
Multiple keys in one object are an implicit AND.
db.rides.find({ $or: [{ status: "Cancelled by Driver" }, { status: "No Driver Found" }] })
Explicit OR. $and, $nor and $not follow the same shape.
db.rides.find({ ratings: { $exists: false } })
Field absence. In this dataset ratings, booking_value and payment_method are absent on non-completed bookings — absence carries meaning.
db.rides.find({ "cancellation.reason": { $regex: /AC/i } })
Pattern match. An unanchored regex cannot use an index efficiently — anchor with /^AC/ when you can.
db.rides.find().sort({ booking_value: -1 }).limit(10)
Top 10 by value. -1 descending, 1 ascending.
db.rides.find().skip(20).limit(10)
Pagination. skip() degrades on large offsets because the server still walks the skipped documents — range queries scale better.
db.rides.distinct("vehicle_type")
Unique values for a field. Returns the seven vehicle types in this dataset.
db.rides.countDocuments({ status: "Cancelled by Driver" })
Count matching a filter — 2698 here, 17.99% of all bookings.

Writing — insert, update, delete

db.rides.insertOne({ booking_id: "CNR999001", status: "Completed", booking_value: 250 })
Insert one document. MongoDB generates _id if you omit it.
db.rides.insertMany([{ ... }, { ... }])
Bulk insert. Add { ordered: false } to continue past individual failures.
db.rides.updateOne({ booking_id: "CNR3000142" }, { $set: { payment_method: "UPI" } })
$set creates the field if it is missing. Forgetting $set and passing a bare object replaces the whole document.
db.rides.updateMany({ status: "Incomplete" }, { $set: { review_flag: true } })
Update every matching document. Always run the same filter through countDocuments() first.
db.rides.updateOne({ booking_id: "CNR3000142" }, { $inc: { booking_value: 50 } })
Atomic increment. $mul, $min and $max work the same way.
db.rides.updateMany({}, { $unset: { review_flag: "" } })
Remove a field from every document. The value passed to $unset is ignored.
db.rides.updateOne({ booking_id: "X" }, { $set: { status: "Completed" } }, { upsert: true })
Upsert — update if present, insert if not. The workhorse of idempotent data loads.
db.rides.deleteMany({ status: "Incomplete" })
Delete matching documents. deleteMany({}) empties the collection but keeps the indexes.

Indexing & Query Plans

db.rides.createIndex({ status: 1 })
Single-field B-tree index. Ascending or descending does not matter for a single-field index.
db.rides.createIndex({ status: 1, booking_value: -1 })
Compound index. Field order follows the ESR rule: Equality, then Sort, then Range.
db.rides.createIndex({ booking_id: 1 }, { unique: true })
Unique index — rejects duplicates and enforces the business key at the database layer.
db.rides.createIndex({ booking_value: 1 }, { partialFilterExpression: { status: "Completed" } })
Partial index. Indexes only the 9,343 completed rides instead of all 15,000 — smaller, faster to maintain.
db.rides.createIndex({ "cancellation.reason": "text" })
Text index for keyword search. A collection may have only one.
db.rides.getIndexes()
List every index. _id_ is always present and cannot be dropped.
db.rides.dropIndex("status_1")
Drop by name. Unused indexes still cost write throughput and disk.
db.rides.find({ status: "Completed" }).explain("executionStats")
The one to memorise. Compare totalDocsExamined with nReturned — if docs examined is far larger, the index is not doing its job.
db.rides.find({ ... }).explain("executionStats").executionStats.executionTimeMillis
Measured execution time. Run it before and after adding an index — that before/after pair is what the project rubric asks for.
COLLSCAN vs IXSCAN
The stage in an explain plan. COLLSCAN reads every document; IXSCAN walks an index. Seeing COLLSCAN on a large collection is the signal to add an index.
db.rides.aggregate([...], { explain: true })
Explain a pipeline. Confirms whether $match and $sort were pushed down to an index.

Aggregation Framework

db.rides.aggregate([{ $match: { status: "Completed" } }])
Always put $match first. It is the only stage that can use an index, and it shrinks everything downstream.
{ $group: { _id: "$vehicle_type", revenue: { $sum: "$booking_value" }, n: { $sum: 1 } } }
Group and aggregate. _id is the grouping key; { $sum: 1 } counts documents.
{ $group: { _id: null, total: { $sum: "$booking_value" } } }
_id: null collapses everything into one bucket — a grand total across the collection.
{ $group: { _id: "$status", avg: { $avg: "$booking_value" }, max: { $max: "$booking_value" } } }
$avg $min $max $first $last $push $addToSet are the accumulators. $avg silently skips missing fields.
{ $sort: { revenue: -1 } } { $limit: 10 }
Rank and truncate. Putting $limit straight after $sort lets the server use a bounded top-K sort.
{ $project: { _id: 0, vehicle: "$_id", revenue: { $round: ["$revenue", 2] } } }
Reshape and rename output. $round makes the result presentable.
{ $match: { bookings: { $gte: 1000 } } }
Guard against small samples. Place after $group to drop thin buckets before ranking by a rate — otherwise 03:00, with 46 bookings, tops the failure-rate chart on noise alone.
{ $addFields: { hour: { $toInt: { $substr: ["$booked_at", 11, 2] } } } }
Derive the booking hour. booked_at is stored as a string in this dataset, so we slice it rather than use a date operator.
{ $addFields: { ts: { $toDate: "$booked_at" } } }
Convert the string to a real BSON date so the $year/$month/$hour operators become available.
{ $dateFromString: { dateString: "$booked_at" } }
The explicit form, with format and onError options for messy input.
{ $cond: [{ $eq: ["$status", "Completed"] }, 1, 0] }
Conditional counting. Wrap in $sum to count a subset inside a group — this is how a completion rate is computed in one pass.
{ $ifNull: ["$booking_value", 0] }
Substitute a default for a missing field. Essential here because failed bookings have no booking_value.
{ $unwind: "$tags" }
Explode an array field into one document per element. Multiplies the document count — always $match before it.
{ $lookup: { from: "drivers", localField: "driver_id", foreignField: "_id", as: "driver" } }
A left outer join. Works, but if you reach for it constantly the documents probably should have been embedded.
{ $facet: { byStatus: [...], byHour: [...] } }
Run several sub-pipelines over the same input in one pass. This is how a whole dashboard payload is produced in a single query.
{ $out: "rides_summary" }
Write pipeline results to a new collection. $merge is the incremental version and must be the last stage.

Redis — Keys, Strings & Expiry

docker run -d --name bdma-redis -p 6379:6379 redis:7-alpine
Start a local Redis. docker stop bdma-redis and docker rm bdma-redis to clean up.
redis-cli redis-cli -h host -p 6379 -a password
Open the shell. PING should answer PONG.
SET dash:total:bookings 15000 GET dash:total:bookings
Set and read a string. Colon-delimited key names are the schema — Redis enforces nothing, so the convention is all you have.
INCR dash:total:bookings INCRBY dash:status:Completed 5
Atomic counters. Safe under concurrency without a transaction — this is the core of compute-on-write.
INCRBYFLOAT dash:revenue:2024-03-14 412.50
Accumulate a money total. Redis stores it as a string and parses on each call.
MGET dash:status:Completed dash:status:Incomplete
Read many keys in one round trip. The difference between one network hop and twenty.
EXPIRE dash:session:abc 3600 TTL dash:session:abc
Set and inspect a time-to-live in seconds. TTL returns -1 for no expiry, -2 if the key is gone.
SETEX cache:report 300 "{...}"
Set with an expiry in one atomic command — the standard cache write.
SCAN 0 MATCH "dash:*" COUNT 100
Use this, never KEYS *. KEYS blocks the single-threaded server across the whole keyspace; SCAN iterates in cursored batches.
DEL key EXISTS key TYPE key
Delete, test and inspect. TYPE is how you find out that a key you thought was a string is actually a hash.
FLUSHDB
Wipe the current database. Fine on your laptop, catastrophic anywhere else.

Redis — Hashes, Lists & Sets

HSET dash:payment UPI 4613 Cash 2593 HGETALL dash:payment
A hash stores an object's fields under one key. Ideal for the payment-method breakdown.
HINCRBY dash:payment UPI 1
Increment a single field without reading the hash first.
LPUSH rides:recent "CNR3000142" LRANGE rides:recent 0 9
A list as a recent-activity feed. Pair with LTRIM rides:recent 0 99 to cap it at 100 entries.
SADD rides:customers:2024-03-14 "CID482910" SCARD rides:customers:2024-03-14
A set of unique members and its exact cardinality. Exact, but memory grows with every distinct member.
SINTER rides:customers:2024-03-14 rides:customers:2024-03-15
Set intersection — customers who rode on both days. SUNION and SDIFF complete the algebra.

Redis — Sorted Sets, Bitmaps & HyperLogLog

ZADD dash:vehicle:revenue 536854 "Premier Sedan"
A sorted set keeps members ordered by score. This is the structure with no clean SQL equivalent.
ZINCRBY dash:vehicle:revenue 412.5 "Go Sedan"
Add to a member's score, creating it at zero if absent. One command per booking keeps a live leaderboard.
ZREVRANGE dash:vehicle:revenue 0 4 WITHSCORES
Top 5, instantly. No sort at read time — the structure is already ordered.
ZRANK dash:vehicle:revenue "Uber XL" ZSCORE dash:vehicle:revenue "Uber XL"
Position in the ranking and the raw score behind it.
SETBIT dash:actives:2024-03-14 482910 1
Bitmap. One bit per user id. Tracking a million users costs ~125 KB — 1,000,000 bits ÷ 8.
BITCOUNT dash:actives:2024-03-14
Count the set bits — daily active users, in one command.
BITOP AND dash:retained dash:actives:2024-03-14 dash:actives:2024-03-15
Retention in two commands. AND the two days, then BITCOUNT the result. OR, XOR and NOT are also available.
PFADD dash:uniques:2024-03-14 "CID482910" PFCOUNT dash:uniques:2024-03-14
HyperLogLog. Approximate unique counts in a fixed 12 KB with ~0.81% error. You cannot list the members back.
PFMERGE dash:uniques:week dash:uniques:2024-03-14 dash:uniques:2024-03-15
Merge HyperLogLogs into a weekly rollup without double-counting.

Redis — Transactions, Pipelines & Operations

MULTI … EXEC
Queue commands and run them as one atomic unit. DISCARD abandons the queue.
WATCH key
Optimistic locking — the following EXEC fails if the watched key changed in the meantime.
INFO memory INFO stats
Server introspection. used_memory_human and keyspace_hits vs keyspace_misses are the two numbers that matter for a cache.
MEMORY USAGE dash:actives:2024-03-14
Bytes consumed by one key. Use it to prove the bitmap memory claim rather than asserting it.
CONFIG SET maxmemory-policy allkeys-lru
Eviction policy. allkeys-lru for a cache, volatile-lru to evict only keys with a TTL, noeviction to fail writes instead of dropping data.
BGSAVE LASTSAVE
Trigger a background RDB snapshot and check when the last one completed. AOF is the lower-loss alternative, at a write-throughput cost.
MONITOR
Stream every command hitting the server. Superb for debugging, and a serious performance hit — development only.

Node.js Clients

npm install mongodb redis
The two official drivers used in the Lecture 18 dashboard project.
const client = new MongoClient(uri); await client.connect();
Connect once at start-up and reuse — the driver pools connections internally. Creating a client per request is the classic mistake.
await db.collection("rides").aggregate(pipeline).toArray()
Run a pipeline from Node. The array you pass is identical to the one you tested in mongosh.
const redis = createClient({ url: "redis://localhost:6379" }); await redis.connect();
Connect the Redis client. Every command returns a promise.
await redis.multi().incr("dash:total:bookings").hIncrBy("dash:payment", "UPI", 1).exec()
Chain updates into one atomic round trip — the shape of the Lecture 18 booking writer.

A note on destructive commands. db.rides.drop(), deleteMany({}) and FLUSHDB appear here because you will need them to reset your lab environment. Build the habit now of running the matching countDocuments() or SCAN first, so that in three years' time you do not run one against production.