NOSQL Course

MongoDB

MongoDB is a database storing data using JSON-based (BSON) documents. You will use JSON everywhere, and there is almost no syntax. This comes at the cost: it's tricky to reference something. You will have to use the ID of a document, or nest a document inside another 😬.

Quick official reference 📚: mongo Shell Quick Reference
Official documentation 🤓: MongoDB documentation


Console

  • On Windows, almost everything is made using the interface, but you may open the console by clicking on the small tab at the bottom of the screen.
  • On Linux, I used mongosh, so I was only using the command-line.

This course will mostly cover Mongo from the point of view of a command-line user, as it's easy to execute something using the interface on Windows, given a command, and the driver usually provides something similar to the commands used in mongosh.


Create a user (optional)

// mongo -u username --authenticationDatabase admin -p
// use admin
// db.createUser({
//     user: "username",
//     pwd:"password",
//     roles:[{ role: "userAdminAnyDatabase", db: "admin"  }]
// })
use database_name
db.createUser( { user: "myuser", pwd: "password", roles: ["readWrite"] })

MongoDB way of doing things

First, you should know that if something does not have values, like a database not having "tables", a table not having "values" or an attribute not having a value, then you won't see it.

  • list all databases show dbs
  • move to database use database_name ("created" if not exists)

Inside a database, you can create collections of documents. A collection could be seen as a table in SQL, and documents could be seen as records/tuples.

  • show collections (remember 🙄, empty = not listed)
  • every document in your collection
    • db.getCollection("name").find()
    • db.name.find()

Note: You can exit with exit 😱.


Documents

A document is a JSON object. It's something like this

{
  "key1": "value",
  "key2": 5,
  "key3": true,
  "key4": [
    {
      "key": "value"
    }
  ]
}

The keys are your attributes, the values are the value for these attributes for this document.

Notes

  • Attributes are not properties of the document itself, so documents in a collection may have different attributes
  • Each document will have a unique attribute _id added, having the type ObjectId
  • The only way to create a relationship between two documents and either
    • Making an embed document (ex: key4)
    • Referencing the _id value of another document

Note: Types that you may use are listed here (new types are Date, ObjectId, ...)


Insert documents

  • Function: insertOne(JSON)
  • Note: create the collection if it does not exist
  • Example
// insert a document { name: "Calistro" }
db.persons.insertOne( { name: "Calistro" } )
// {"_id": "617d8282a6e50c611c8e3c16", "name": "Calistro"}
  • Function: insertMany(JSONArray)
  • Note: create the collection if it does not exist
  • Example
db.persons.insertMany( [ {name: "Suwan", not_used: true }, {name: "Mynasitra"} ] )
// two new documents

Find documents

You may find(JSON, JSON) or findOne(JSON, JSON).

  • the first value is the filter, a SQL WHERE (empty object=default=No restriction)
  • the second value is the project, a SQL SELECT (empty=default=Select *)
Examples of SQL vs MongoDB

Note that I won't give examples for findOne, as this is the same as find, but return one row (in SQL, it would be LIMIT 0,1).

SQLMongoDB
SELECT name FROM persons
db.persons.find({}, { name: true })
SELECT * FROM persons
db.persons.find()
SELECT name FROM persons WHERE name = "Calistro"
db.persons.find({ name: "Calistro" }, { name: true })
SELECT * FROM persons WHERE name = "Calistro"
db.persons.find( { name: "Calistro" } )
SELECT * FROM persons WHERE name >= "Calistro"
db.persons.find({ name: { $gte: "Calistro" } })
// you can use $gte (>=) $gt (>) $eq (=) $ne (<>) $lt (<) $lte (<=)
-- no equivalent
db.persons.find({ name: { $exists: true } })
SELECT * FROM persons WHERE name = "Calistro" OR
                            name = "Luna"
db.persons.find({ $or: [ 
  { name: 'Calistro' },
  { name: 'Luna' }
] })
// $or (OR), $and (AND), $NOR (not any of the predicates)

⚠️ Pro tip ⚠️: even if you may think that the field _id is a string, it's an ObjectId. Use ObjectId(string) to cast a string to an ObjectId.

// no matches
db.persons.find( { _id: "617d8282a6e50c611c8e3c16" } )
// 1 matches, same id ✨, "same" request ✨
db.persons.find( { _id: ObjectId('617d8282a6e50c611c8e3c16') } )

Pro tip: when using projection, or in some cases such as updating something, you are setting a bool at true for a column. Well, lazy as we are, we are also using 1 or "" instead of true.


Update and Delete

  • use UpdateOne(JSON, JSON) or UpdateMany(JSONArray, JSON)
Set attributes
db.persons.updateOne(
    // select
    { _id: ObjectId('617d8282a6e50c611c8e3c16') },
    // set selected
    {
        // create name with value, or update field
        $set: { "name": "Mr. Calistro" }
    }
)
Unset attributes
db.persons.updateOne(
    // select
    { _id: ObjectId('617e5dd65f15583a7c4e06ea') },
    {
        // delete this field for this document
        $unset: { "not_used": true }
    }
)
  • use DeleteOne(JSON) or DeleteMany(JSON)
db.persons.deleteOne({ _id: ObjectId('617d8282a6e50c611c8e3c16') })

Methods

In MongoDB, you can chain a lot of calls, unless the call is "terminal".

SQL conceptMongoDB
COUNT(*)
db.persons.find().size() // terminal
LIMIT 0,1
db.persons.find().skip(0).limit(1)
db.persons.find().skip(countPerPage * page).limit(countPerPage)
ORDER BY
db.persons.find().sort({name: 1}) // ASC
db.persons.find().sort({name: -1}) // DESC

Embed documents

You should read the official documentation.

db.persons.updateOne(
    { name: "Calistro" },
    {
        $set: {
            cars: {
                main: {
                    model: "Something"
                }
            }
        }
    }
)
db.persons.findOne({ name: "Calistro" })
// cars = {"main": {"model": "Something"}}
db.persons.findOne({ name: "Calistro" })
// cars = {"main": {"model": "Something"}}

For embed items, you can use . (dot) to use a field everywhere we used fields such as name. Note that this time, the name of the field must be between "" (quotes).

db.persons.findOne(
    { "cars.main.model": "Something" }
)
// Calistro ...

Note: this is one of the ways to make a relationship between documents. You are simply storing a document inside another. You could give an ObjectId to main referencing another document inside another collection (ex: cars).


Sources