Expert: design, speed and engines · Procedures, triggers, SQL engines and NoSQL · topic 56 of 62 · 40 min
MongoDB and Redis in practice
Explanation
MongoDB: documents and collections
In MongoDB, a document is one record written like JSON (stored as BSON, a binary form). Documents live in a collection, which is like a table without fixed columns. Each document has a unique _id. One document can hold nested objects and arrays, so an order can carry its items inside it. You talk to MongoDB from the mongosh shell or from a driver in your language.
db.food_orders.insertOne({
customer: "Asha", city: "Pune", amount: 640,
items: [ { name: "Paneer Tikka", qty: 1 }, { name: "Butter Roti", qty: 4 } ]
})
// { acknowledged: true, insertedId: ObjectId('...') }find: filters and projection
find takes a filter (which documents) and an optional projection (which fields). Operators start with $: $gt (greater than), $lt, $in, $ne and so on. A field listed with 1 is kept; _id: 0 hides the id.
db.food_orders.find(
{ city: "Pune", amount: { $gt: 500 } },
{ _id: 0, customer: 1, amount: 1 }
)
// [ { customer: 'Asha', amount: 640 }, { customer: 'Asha', amount: 760 } ]
// The same question in SQL:
// SELECT customer, amount FROM food_orders WHERE city = 'Pune' AND amount > 500;updateOne with $set and $inc
db.food_orders.updateOne(
{ _id: ObjectId("...") },
{ $set: { status: "Delivered" }, $inc: { points: 10 } }
)
// { acknowledged: true, matchedCount: 1, modifiedCount: 1, ... }The aggregation pipeline
An aggregation pipeline is a list of stages; each stage passes its output to the next, like an assembly line. $match is WHERE, $group is GROUP BY, $sort is ORDER BY.
db.food_orders.aggregate([
{ $match: { amount: { $gte: 400 } } },
{ $group: { _id: "$city", total: { $sum: "$amount" }, orders: { $sum: 1 } } },
{ $sort: { total: -1 } }
])
// [ { _id: 'Pune', total: 1850, orders: 3 }, { _id: 'Mumbai', total: 920, orders: 1 } ]
// SQL: SELECT city, SUM(amount) AS total, COUNT(*) AS orders
// FROM food_orders WHERE amount >= 400 GROUP BY city ORDER BY total DESC;Embedding vs referencing
Embedding means putting related data inside the document (the order's items). Referencing means storing an id that points to another document (the customer_id), like a foreign key. Embed what is read together and stays small; reference what is shared, many-to-many, or grows without limit. A single document can be at most 16 MB, so an ever-growing array of reviews belongs in its own collection.
Redis: a fast key-value store
Redis keeps data in memory, so reads and writes take well under a millisecond. Values have types: strings, hashes, lists, sets and sorted sets. Here are the everyday commands, typed in redis-cli:
SET session:abc123 "user:42" EX 1800 # cache for 30 minutes
GET session:abc123 # "user:42"
TTL session:abc123 # (integer) 1800, counting down
INCR views:home # (integer) 1, then 2, 3...
INCR rate:user42:1230 # requests this minute
EXPIRE rate:user42:1230 60 # forget the counter after 60 s
HSET user:42 name "Asha" city "Pune" # (integer) 2
HGET user:42 city # "Pune"
LPUSH jobs "send-invoice-101" # a simple queue
RPOP jobs # "send-invoice-101"
ZADD leaderboard 980 asha 1200 ravi 1100 imran
ZRANGE leaderboard 0 2 REV WITHSCORES # ravi 1200, imran 1100, asha 980Two more features in one line each. Pub/sub: PUBLISH sends a message to every client that ran SUBSCRIBE on that channel, with no storage. Persistence: Redis can save snapshots (RDB) and/or an append-only log of writes (AOF), but it is usually used as a cache in front of a main database, not as the only copy. Valkey is an open-source fork of Redis that accepts the same commands.
A bridge you can run: JSON in SQLite
Relational engines can store documents too. In SQLite, json_extract(doc, '$.city') reads a field from JSON text, and the table-valued function json_each(doc, '$.items') turns an array into rows. PostgreSQL's JSONB and MySQL's JSON type do the same with their own operators.
SELECT f.id, json_extract(item.value, '$.name') AS item, json_extract(item.value, '$.qty') AS qty
FROM food_orders AS f, json_each(f.doc, '$.items') AS item
WHERE f.id = 1;
-- 1 | Paneer Tikka | 1
-- 1 | Butter Roti | 4Common mistakes
- In MongoDB, calling updateOne with a plain object instead of $set, which replaces the whole document. Use update operators.
- Forgetting EX on cache keys in Redis, so memory fills with stale data. Give every cache key an expiry.
- Treating Redis as the only copy of important data without understanding its persistence settings.
- In SQLite, forgetting the $ in paths: json_extract(doc, 'city') is an error; write '$.city'.
- Embedding data that is shared by many documents (a customer's address in every order) and then updating it in only one place.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
MongoDB and Redis in practice · 1/8
Let us see two popular NoSQL databases in action: MongoDB for documents and Redis for fast keys.
Prefer a video by a teacher? Find free videos on “mongodb find aggregate tutorial and redis commands caching leaderboard” on YouTube ↗
In real life: A leaderboard and a cache for a quiz app
A learning or fantasy-sports app with a live leaderboard often keeps scores in a Redis sorted set: ZINCRBY adds points as answers come in, and ZRANGE ... REV returns the top ten instantly. User profiles and quiz content may live in MongoDB or PostgreSQL, with popular quiz pages cached in Redis for a few minutes using SET with EX. The main database stays the source of truth; Redis makes the hot paths fast.
ZINCRBY leaderboard 50 asha # "1030"
ZRANGE leaderboard 0 9 REV WITHSCORES # top 10 with scoresPractice (0/4 done)
Which SQL matches this MongoDB query? db.food_orders.find({ city: { $in: ["Pune", "Mumbai"] }, amount: { $lt: 800 } }, { _id: 0, customer: 1 })
The table food_orders(id, doc) stores each order as JSON text. Translate the Mongo query find({ city: "Pune", amount: { $gt: 500 } }) into SQLite: return id, the customer (as customer) and the amount (as amount) of Pune orders with amount greater than 500. Use json_extract(doc, '$.field').
Unpack the items arrays. For every order from Pune, return one row per item with three columns: id (the order id), item (the item's name) and qty. Use json_each(food_orders.doc, '$.items') in the FROM clause and json_extract on its value column.
Write the SQL version of this pipeline: [{ $group: { _id: "$city", orders: { $sum: 1 }, total: { $sum: "$amount" } } }, { $sort: { total: -1 } }]. Return city, orders and total from food_orders, highest total first.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Choosing a database, and keeping it safeProcedures, triggers, SQL engines and NoSQL · same module
- MySQL vs PostgreSQL vs SQLite vs SQL ServerProcedures, triggers, SQL engines and NoSQL · same module
- NoSQL and MongoDBBackend Development · NoSQL, ORMs and data features
- Caching and RedisBackend Development · Performance and reliability
- What is a database?Backend Development · Databases and SQL basics
- Popular stacks and how to chooseFull-Stack Development · What full-stack development is
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
In MongoDB, what does { $inc: { points: 10 } } do in an updateOne?
Answer: Adds 10 to points (creating it if missing)
$inc adds to a numeric field; $set writes a value.
Which aggregation stage plays the role of SQL's WHERE?
Answer: $match
$match filters documents; put it early so later stages see fewer documents.
Which Redis command caches a value for 10 minutes?
Answer: SET page:home "<html>" EX 600
SET with EX gives the key an expiry in seconds.
Which Redis structure suits a game leaderboard?
Answer: A sorted set
Sorted sets keep members ordered by score; ZINCRBY adds points and ZRANGE ... REV reads the top.
A blog post receives thousands of comments over the years. In MongoDB, how should you store them?
Answer: In their own collection, each comment referencing the post id
An array that grows without limit can hit the 16 MB document limit and makes every read heavy; reference instead.
In MongoDB, what is a collection?Related
Answer: A group of documents, like a table
A collection holds many documents, the way a SQL table holds many rows.
From NoSQL and MongoDB
What does SET key value EX 60 do in Redis?Related
Answer: Saves the value and makes it expire after 60 seconds
EX sets the expiry in seconds. After 60 seconds Redis removes the key.
From Caching and Redis
What is the PostgreSQL way to list names per group, separated by commas, in order?Related
Answer: STRING_AGG(name, ', ' ORDER BY name)
PostgreSQL uses STRING_AGG with ORDER BY inside the brackets.
Why do apps store users in a database and not in an array in the code?Related
Answer: An array is lost when the server restarts
An array lives in memory and disappears on restart. A database keeps data safely on disk.
From What is a database?
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Procedures, triggers, SQL engines and NoSQL” before its exam: Choosing a database, and keeping it safe.
- Module 11 exam: procedures, triggers, engines and NoSQL · 13 questions · 25 min, without help. 6 topics of this module still to finish first.
- Then the next module, Interview questions and practice projects. 62 topics left in the course before the final exam.
← Previous: NoSQL: documents, key-value, wide-column and graph