#100DaysOfMERN - Day 26

#100DaysOfMERN - Day 26

·

15 min read

✏ Starting with MongoDB

This is going to be a huge chapter, I'd estimate at least 10 days, because there's a whole palette of new tools and packages involved:

I've already mentioned this idea of mine in a previous post - that I want to create my very own #100DaysOfMERN app, to keep track of the topics of each blog, together with a list of keywords, so I can later easily search through them if I want to look something up again.

I'm going to build the whole package including a pretty frontend, backend and database. This is SO meta, I just love the idea. Plus, the list at the end of each of my posts grows longer and longer, and it already gets messy. I'd like to replace that with just a link to the app.


✏ Resources

To start, I'll losely follow along two of my favourite YouTubers and their tutorials:

This is from July 2019, so quite up-to-date, but being "just" a crash course, not super detailed. It's focussed on handling your database from the Shell, but it also introduces you to Compass and Atlas.

This course is from January 2017, and in terms of software development already outdated in some parts, but nevertheless an excellent playlist (and I haven't met any severe obstacles yet due to it not being from 2 weeks ago). It's focussed on how to connect, manipulate and manage your database with your JavaScript code.

It also includes an introduction to Mocha, which I'd really like to pick up along the way.


✏ Installation & Setup

I'll stick with a local database for now, so I'll need MongoDB Server, Compass and Shell (Compass should be included in the Server installation, but I've installed so much stuff in the past couple of days that I honestly can't remember every detail).

After installation, the first step is to

  • create a folder called data in the root directory (C:\ for me)
  • create a subfolder called db within

MongoDB always has to be running in the background whenever you work with it, so to run Mongo from the Shell, open your favourite terminal and move to

cd C:\programs\MongoDB\Server\4.4\bin

The path might be different on your machine, but you should be able to find the right place. Note that the version number needs to match what you've installed. Then just type:

mongo

to start (exit to terminate).

The default port for Mongo is 27017, so checking localhost:27017 should now show an indication that you have it running (if the absence of an error in the terminal isn't enough proof).


✏ MongoDB Compass

Compass is a GUI that lets you interact with your database. To establish a new connection, you click on, well, "New Connection", and it wants you to paste in a connection string. The syntax of the example string makes sort of sense, but clicking on "Fill in connection fields individually" is easier at the beginning.

  • Host: localhost
  • Port: 27017
  • Authentication: none

I think those are even the defaults. If you now click on "Connect", you'll find that there's already three databases in there: admin, config and local. Those can be ignored (and probably shouldn't be messed with unless you know what you're doing, which I don't at the moment).

The interface now offers a few familiar options, if you've worked with phpMyAdmin before. You can create a new database, collections, and edit data.

The data structure is completely different though, compared to a relational SQL database. Instead of dealing with tables, rows and columns, you'll be handling JavaScript or JSON objects, and use JavaScript syntax to create/read/update/delete data. How lovely.

One big advantage of this document-based approach is that you'll deal less with comparing different tables through primary and secondary keys when making queries (and the annoying details like the difference between INNER JOIN and OUTER JOIN).


✏ Structure of a noSQL Database

First of all - the "no" in "noSQL" doesn't actually mean "no", it stands for "not only" (who would've thought).

Generally, a database consists of collections (= tables). As I'm planning my #100DaysOfMERN database, I only have one collection in it: my posts (I've clumsily named it collectionPosts to make it crystal clear what I'm working with when writing code).

This collection will be filled with documents (= records), where every entry will represent the data of one blog post. Those will be formed using a certain model; they will all have a title, and an array of keywords related to that post, maybe also an entry for the URL, a short summary, etc.

The model follows a schema (= template), where I define the keys, and the data types of the values.

(Note: the items/records in a collection don't all have to follow the same model/schema, noSQL databases don't have this restriction - but I couldn't come up yet with an example why you'd want to fill a collection with objects of different structures)

nosql-database-structure.png

If you're totally new to this, it will become clearer when you see some code later on. Whenever you create a new record, that new object will be structured according to the schema it's based on.

But before jumping into JavaScript code and Mongoose, I'd like to include a little cheat sheet that shows how to handle MongoDB from the Shell.


✏ MongoDB Shell

After moving to C:\Programs\MongoDB\Server\4.4\bin (or similar) in the terminal, start with typing mongo.

There's tons of cheat sheets for MongoDB, but a particularly excellent one is this from developer.mongodb.com *). Below is my slightly modified and shortened version with a few more comments. I've left out the ones that don't make sense to me yet, so it's by no means complete, but it's unlikely anyway that I'll later use the Shell much to handle my data. It gives a good preview though about the syntax used for CRUD operations.

*) The sheet is from September 2020, but some commands are by now disencouraged/cause deprecation warnings (read more on mongoosejs.com.

Connect MongoDB

// mongo connects to mongodb://127.0.0.1:27017 by default
mongo --host <host> --port <port> -u <user> -p <pwd> // omit the password if you want a prompt
mongo "mongodb://192.168.1.1:27017"
// MongoDB Atlas
mongo "mongodb+srv://cluster-name.abcde.mongodb.net/<dbname>" --username <username>

Helpers

// show all databases
show dbs
// print the current database
db 
// show all collections of that db
show collections

Databases and Collections

// switch to database / create a new one if it doesn't exist
// new database will only be saved if you also create a collection within
use postsDB

// create collections
db.createCollection('coll')

// drop database (with great power...)
db.dropDatabase()

// drop collection
db.coll.drop()

Create

// create/insert records
db.coll.insert({title: "Day 1"})
db.coll.insertOne({title: "Day 1"})
db.coll.insertMany([{title: "Day 1"}, {title: "Day 2"}])

// include date of creation
db.coll.insert({title: "Day 1", date: Date()})
db.coll.insert({title: "Day 1", date: ISODate()})

// each entry will automatically get an _id property
// with a unique identifier

Read

// Find
db.coll.find()  // find all
db.coll.find().pretty() // format output
db.coll.find({category: "Mongo", author: "jsdisco"}) // implicit logical "AND"

db.coll.findOne() // returns the first document
db.coll.findOne({category: "Mongo"}) // returns the first match

// Loop
db.coll.find().forEach(function(doc) {print('Blog: ' + doc.title)})

// Count
db.coll.count() // unfiltered count of the whole collection
db.coll.countDocuments({category: "Mongo"})

// Comparison
db.coll.find({views: {$gt: 10}}) // greater than
db.coll.find({views: {$gte: 10}}) // greater than or equal
db.coll.find({views: {$lt: 30}}) // lesser than
db.coll.find({views: {$lte: 30}}) // lesser than or equal
db.coll.find({views: {$ne: 1}}) // not equal
db.coll.find({views: {$in: [20, 30]}}) // in array
db.coll.find({views: {$nin: [20, 20]}}) // none in arry

// Logical
db.coll.find({category: {$not: {$eq: "Shell"}}})
db.coll.find({$or: [{day : 2}, {day : 5}]})
db.coll.find({$nor: [ {category: "Mongo"}, {author: "jsdisco"}]})

// Regex
db.coll.find({author: /^jsdisco/})   // regex: starts by letter "j"
db.coll.find({author: /^JsDisco$/i}) // regex case insensitive

// Limit results
db.coll.find().limit(5)

// Skip results
db.coll.find().skip(10)

// Sort
db.coll.find().sort({title: 1}) // ascending
db.coll.find().sort({title: -1}) // descending

Update

db.coll.update({_id: 1}, {category: "Shell"}) // WARNING! Replaces the entire document

// Use operators (examples)
// $set operator: keep existing properties instead of replacing the document
db.coll.updateOne({_id: 1}, {$set: {category: "Shell"}})
// $unset operator: delete a property (not sure if the value matters here)
db.coll.updateOne({_id: 1}, {$unset: {category: "Shell"}})
// $rename operator: rename a property
db.coll.updateOne({_id: 1}, {$rename: {title: "heading"}}
// $inc operator: increment a value by a certain amount
db.coll.updateOne({_id: 1}, {$inc: {views: 10}})

// Upsert (if record exists - update, if not, insert new record)
db.coll.updateOne({_id: 1}, {$set: {title: "Day 1"}}, {upsert: true})

Delete

db.coll.deleteMany({}) // WARNING! Deletes all the docs but not the collection itself
// delete specific entry
db.coll.deleteOne({_id: 1})

Restrictions for names

Note that there's certain restrictions for naming databases and collections. According to that link, you could use a hyphen, but you'll just make your life harder because you can't use dot notation. Instead of this:

db.my-collection.find()

you'd have to type out this:

db['my-collection'].find()

As MongoDB lives in JavaScript land, camelCase seems to be a good choice for naming collections and databases, but a lot of that is personal preference. If you come from a SQL database background, you might prefer underscores as separators.


✏ Recap

I've learned

  • how to install MongoDB and Compass
  • fundamentals: structure of noSQL databases
  • how to use the Mongo Shell for CRUD operations

✏ Next:

  • setting up my #100DaysOfMERN app database
  • Mongoose

✏ Thanks for reading!

I do my best to thoroughly research the things I learn, but if you find any errors or have additions, please leave a comment below, or @ me on Twitter. If you liked this post, I invite you to subsribe to my newsletter. Until next time 👋


✏ Previous Posts

  • Day 1: Introduction, Node.js, Node.js in the terminal
  • Day 2: npm, node_modules, package.json and package-lock.json, local vs global installation of packages
  • Day 3: Create a React app without create-react-app, Webpack, Babel
  • Day 4: npx and cowsay
  • Day 5: npm vs. npx, npm audit, semantic versioning and update rules
  • Day 6: Call stack, event loop, JavaScript engine, JavaScript runtime
  • Day 7: Call stack and event loop in Node.js, setImmediate()
  • Day 8: setImmediate(), process.nextTick(), event loop phases in Node.js
  • Day 9: Network requests with XMLHttpRequest and callbacks
  • Day 10: Promises
  • Day 11: Network requests with XMLHttpRequest and Promises
  • Day 12: React Quiz App part 1
  • Day 13: React Hangman
  • Day 14: FullStackOpen course 1: details of GET and POST requests, request headers
  • Day 15: React Hangman: Trigger fetch with click event callback vs useEffect
  • Day 16: REST API and CRUD
  • Day 17: Boring Book App part 1: React Frontend, Express Backend, GET requests, CORS
  • Day 18: Boring Book App part 2: POST request, File System API
  • Day 19: Boring Book App part 3: Request Parameters, DELETE request
  • Day 20: Boring Book App part 4: PUT request
  • Day 21: Express JS vs Vanilla JS part 1: Server setup, routes
  • Day 22: Express JS vs Vanilla JS part 2: Serve static files with Vanilla Server
  • Day 23: Express JS vs Vanilla JS part 3: Serve static files with Express Server, Middleware
  • Day 24: Express JS: express.Router, Postman
  • Day 25: Express JS: express-handlebars