Until now the Job Tracker has kept its data in an array, which disappears when the server stops. A database keeps data safely on disk and lets many requests read and change it at once, quickly and without corrupting it. MongoDB is a *document database*. Instead of rows in tables, it stores documents, which look almost exactly like the JavaScript objects you already use, which is one of the reasons it fits so naturally into a JavaScript stack.
Documents, collections and databases
- A document is one record:
{ company: 'Acme', role: 'Dispatcher', status: 'applied', tags: ['night shift'] }. It can hold nested objects and arrays. It is stored in a format called BSON (binary JSON) that adds types such as dates and ids. - A collection is a group of documents, similar to a table. Our app will have a
userscollection and ajobscollection. - A database holds collections. Our app uses one called
job_tracker. - Every document has an `_id`, a unique value that MongoDB creates for you if you do not (an
ObjectId, which is displayed as 24 hexadecimal characters).
How is that different from SQL?
In a SQL database every row in a table has the same columns, set in advance, and you spread related data across tables and join them when reading. In MongoDB documents in the same collection may have different fields, and you often keep related data together inside one document. That flexibility is useful when the shape of data changes, but it is also a trap: without rules, a collection can fill up with inconsistent documents. That is why in lesson 16 we add a schema with Mongoose. Neither model is better in every case. SQL databases are excellent when data has many relationships, strict rules and reports across tables; document databases are comfortable when you read and write whole objects, as most web apps do.
Get a database: two options
# Lesson 14: set up MongoDB
Pick one:
**A. MongoDB Atlas (free cloud database, nothing to install)**
1. Create an account at mongodb.com/atlas and make a free (M0) cluster.
2. Database Access: add a user with a password. Network Access: allow your current IP address.
3. Connect > Drivers: copy the connection string. It looks like `mongodb+srv://USER:PASSWORD@cluster0.xxxxx.mongodb.net/job_tracker`.
**B. MongoDB Community Edition on your computer**
1. Install it from mongodb.com/try/download/community and start the service.
2. Your connection string is `mongodb://127.0.0.1:27017/job_tracker`.
Then try the shell script: `mongosh "<connection string>" examples/14-mongodb/queries.mongosh.js`.
MongoDB Atlas is the hosted service by the makers of MongoDB, and its free tier is enough for this course. You do not install anything, and later the same cluster can serve your deployed app. Two settings catch beginners out: the Network Access list must include your current IP address (or, for learning only, you may allow all addresses, but never do that for real data), and the database user is separate from your Atlas login. Community Edition runs on your own computer and works offline; it is free too.
Try the shell
mongosh is MongoDB's command line. The script below inserts three jobs, searches them in several ways, changes one, groups and counts them, deletes one and drops its demo database. Read each part, because the operations are the same ones Mongoose will perform for you.
// Lesson 14: MongoDB in the shell.
// Run with mongosh (comes with MongoDB): mongosh "<your connection string>" examples/14-mongodb/queries.mongosh.js
// This uses a database called job_tracker_demo and drops it at the end, so it is safe to run.
const db = db.getSiblingDB('job_tracker_demo');
// INSERT: a collection is created the first time you write to it
db.jobs.insertMany([
{ company: 'Northwind Bank', role: 'Teller', status: 'applied', salary: { min: 20000, max: 26000 }, tags: ['bank', 'day shift'] },
{ company: 'Acme Logistics', role: 'Dispatcher', status: 'interview', salary: { min: 18000, max: 24000 }, tags: ['night shift'] },
{ company: 'Greenfield Foods', role: 'Line Supervisor', status: 'saved', tags: ['factory'] },
]);
// FIND: filter, project (choose fields), sort, limit
print('--- applied or interview');
printjson(db.jobs.find({ status: { $in: ['applied', 'interview'] } }, { _id: 0, company: 1, status: 1 }).sort({ company: 1 }).toArray());
print('--- nested field and array');
printjson(db.jobs.find({ 'salary.min': { $gte: 19000 }, tags: 'bank' }, { _id: 0, role: 1 }).toArray());
// UPDATE: $set changes fields, $push adds to an array
db.jobs.updateOne({ company: 'Greenfield Foods' }, { $set: { status: 'applied' }, $push: { tags: 'full time' } });
// AGGREGATE: group and count (this is how the stats screen will work)
print('--- jobs per status');
printjson(db.jobs.aggregate([{ $group: { _id: '$status', count: { $sum: 1 } } }, { $sort: { count: -1 } }]).toArray());
// DELETE
db.jobs.deleteOne({ company: 'Acme Logistics' });
print('--- left:', db.jobs.countDocuments());
db.dropDatabase();
- `insertMany` adds documents; the collection is created on first write.
- `find(filter, projection)` returns matching documents. Filters can use operators that start with
$:$in,$gte,$or. A dotted path such as'salary.min'reaches inside a nested object, andtags: 'bank'matches an array that *contains*'bank'. - `updateOne(filter, update)` needs an update operator like
$setor$push; without one MongoDB would replace the whole document. - `aggregate` runs a pipeline of stages.
$groupwith$sum: 1counts documents per value. You will build the Job Tracker's statistics screen with exactly this in lesson 19.
Your task
- Create an Atlas free cluster (or install Community Edition) and copy the connection string.
- Install
mongoshand run the script against your database:mongosh "<connection string>" examples/14-mongodb/queries.mongosh.js. - Edit the script: add a
locationto each job and find only the jobs in one place. - Write a pipeline that finds the highest
salary.maxper status.
How to check it worked
- The script prints the filtered jobs, the grouped counts and “left: 2”.
- You can connect with
mongoshand list databases withshow dbs.
Summary
- MongoDB stores JSON-like documents in collections; every document has an
_id. - Use Atlas (hosted) or Community Edition (local). Keep the connection string secret.
- Filters,
$set/$pushupdates and aggregation pipelines are the core operations.