--- tags: ironhack, lecture, --- <style> .markdown-body img[src$=".png"] {background-color:transparent;} .alert-info.lecture, .alert-success.lecture, .alert-warning.lecture, .alert-danger.lecture { box-shadow:0 0 0 .5em rgba(64, 96, 85, 0.4); margin-top:20px;margin-bottom:20px; position:relative; ddisplay:none; } .alert-info.lecture:before, .alert-success.lecture:before, .alert-warning.lecture:before, .alert-danger.lecture:before { content:"👨‍🏫\A"; white-space:pre-line; display:block;margin-bottom:.5em; /*position:absolute; right:0; top:0; margin:3px;margin-right:7px;*/ } b { --color:yellow; font-weight:500; background:var(--color); box-shadow:0 0 0 .35em var(--color),0 0 0 .35em; } .skip { opacity:.4; } </style> ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) # MongoDB | Shell & CRUD operations ## Learning goals After this lesson, you will be able to: - Use the MongoShell to Create, Read, Update and Delete Documents ## Intro ![](https://i.imgur.com/NzalR30.png) :::info lecture CRUD ::: When working with any Database, there are a few operations that we will always need to use. These operations are so popular that we have an acronym for it: [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) stands for `create`, `read`, `update` and `delete`: ![](https://i.imgur.com/CRowB2i.png) These are the basic operations to persist data in our Database and with them, we can do anything we want. Ready? ## Quick Recap ### Database commands We can launch the mongoShell by simply typing `mongo` in our terminal. Remember that each mongo server can hold many **databases**. | Database Command | Explanation |------------------|-------------- | `show dbs` | List all the databases inside our mongo server | `use <dbName>` | This will switch to the database `dbName` or create it if it doesn't exist | `db` | Show the name of the current database ### Collection commands A Database is composed of different **collections**. This lesson will be focused on how to create, read, update and delete documents inside a collection. But first of all, let's take a look at a couple of useful collection commands: | Collection Command | Explanation |--------------------|-------------- |`show collections ` | List all the collections inside the current database ### JSON Documents Each Collection is composed of different **documents**. A document is nothing more than a JSON object, with different **keys** and its associated values. We can identify a document by its `_id` property that mongo generates automatically for us. **Example:** ```javascript { "_id" : ObjectId("58401d8b71caea72370262c1"), "name" : "John Doe", "age" : 25, "address": { "country": "Japan", "address": "#901, 74-1 Yamashita-cho, Naka-ku" } } ``` ## Create | Inserting Documents Create commands allow us to insert new documents into a collection. If the collection doesn't exist, the operation will create it. | Insert Command | Summary |----------------|-------------------- | db.collection.insertOne(doc) | Adds a document to the collection | db.collection.insertMany([docs]) | Adds one or more documents to the collection ### insertOne() The [db.collection.insertOne](https://docs.mongodb.com/v3.2/reference/method/db.collection.insertOne/) command inserts a single document into the collection. **Example:** ```javascript > db.enemies.insertOne({ "name": "Blinky", "color": "Red" }) // The command returns an object with the following structure // // { // "acknowledged" : true, // "insertedId" : ObjectId("583c63f96fa7d619e2c574e6") // } ``` :::info **Notes** - Since we don't have a collection called `enemies`, it will be created automatically - As you can see, the document didn't have an `id`, so MongoDB created one for us ::: **Another Example** ```javascript > db.enemies.insertOne({ "_id": 10, "name": "Pinky", "color": "Pink", "status": "alive" }) // The command returns an object with the following structure // // { // "acknowledged" : true, // "insertedId" : 10 // } ``` Notice how the second document we inserted (`pinky`) has an extra field (`status`). MongoDB allows us to have a lot of flexibility with the schema, but we should be careful with it. ### insertMany() [db.collection.insertMany()](https://docs.mongodb.com/v3.2/reference/method/db.collection.insertMany/) allows us to insert more than one document at the same time. `insertMany()` receives an array of documents as a paramater. **Example** ```javascript > db.enemies.insertMany([ { "name": "Inky", "color": "Cyan", }, { "name": "Clyde", "color": "Orange" } ]) // The command returns an object with the following structure // // { // "acknowledged" : true, // "insertedIds" : [ // ObjectId("583c645b6fa7d619e2c574e7"), // ObjectId("583c645b6fa7d619e2c574e8") // ] // } ``` The return object will have a key `insertedIds`, which contains an array with the `ids` of the inserted elements ### About insert() The [db.collection.insert()](https://docs.mongodb.com/v3.2/reference/method/db.collection.insert/) method inserts a single document in the same way that `insertOne()` does. :::danger :bomb: **Warning Notice** The [insert](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#insert) method is deprecated (should not be used because it will be removed soon) in the **Node.js mongo driver**. You can use it in MongoShell, but we strongly recommend to use the `insertOne` and `insertMany` methods above in order to get used to the syntax. ::: ## Read | Finding/Listing Documents The idea behind the read operation is that we should be able to retrieve `documents` from the database and read their contents. The basic method to find documents is [db.collection.find()](https://docs.mongodb.com/v3.2/reference/method/db.collection.find/) where you can specify two optional arguments: :::info `db.collection.find( <query_filter>, <projection> )` ::: - `<query_filter>` an object that specifies which documents to return - `<projection>` an object that allows us to return only certain fields from those documents ![](https://i.imgur.com/aCPgpwr.png) ### Query Filters #### Select all documents To get all documents from a collection is as simple as specifying an empty object as the first argument. ``` > db.enemies.find({}) ``` :::info :bulb: If we only pass one argument we can omit the empty object. `db.enemies.find()` is the same as `db.enemies.find({})` ::: #### Specify Equality Condition Equality conditions will select all documents where the `<field>` matches the exact `<value>` ``` > db.collection.find({<field>: <value>}) ``` **Example:** ``` > db.enemies.find({"name": "Blinky"}) ``` :::warning :cactus: Keep in mind that `<value>` is case sensitive, so `'Blinky'` is not the same as `'blinky'`. ::: #### Advanced Conditions We can specify conditions like greater than or less than, to do so we use query operators like: ```javascript > db.collection.find( { <field>: { <operator>: <value>} } ) ``` Mongo provides us with a comprehensive list of [query operators](https://docs.mongodb.com/manual/reference/operator/query/), but for now, let's look at some of the most useful ones: Mongo Operators | Description :----------------:|----------------- `$eq` | equal to `$ne` | not equal to `$gt` | greater than `$gte` | greater than equal `$lt` | less than `$lte` | less than equal **Example** Imagine that we have a collection where documents have the following structure: ```javascript [ { name: "Popeye", age: 30 }, { name: "Mickey Mouse", age: 35 }, { name: "Batman", age: 24 } ] ``` If we want to select all the users who are older than 32 years, we can do the following query. ``` db.user.find({"age": { $gt: 32 }}) ``` ### Limiting Fields | Projections The Find method's second argument, which we mentioned earlier, is called a [projection](https://docs.mongodb.com/v3.2/tutorial/project-fields-from-query-results/), and it allows us to specify which fields should be returned from each document in the result. :::info **Using projections makes our database queries faster**. When MongoDB has to retrieve less fields, it's less work for the computer to do and less information to transfer over the network so the query takes less time. ::: Let's see how to use the projection. The projection must be an object with either: - All the fields we want to include (set them to `1`) - All the fields we want to exclude (set them to `0`) The projection cannot combine `0` and `1` at the same time except for the `_id` that can be `0` when others are `1`. :::warning By default "_id" is set to `1`, so if we don't want to show it we have to specify `_id: 0`. ::: Example: ```javascript > db.enemies.find({},{ "name": 1, "_id": 0 }) ``` As you can see we specified as a projection {`"name": 1`, `"_id": 0`}. Mongo will return the `name` property of each documents, but not their `_id`: ```javascript { "name" : "Blinky" } { "name" : "Pinky" } { "name" : "Inky" } { "name" : "Clyde" } ``` ### Independent Practice Let's introduce the following documents to our database so that we can all start with the same data. ```javascript { name: "Sue", age: 19, phone: { personal: "555-123-123", work: "555-456-456", ext: "2342" }, privileges: "user", favorites: { artist: "Picasso", food: "pizza" }, finished: [ 17, 3 ], badges: [ "blue", "black" ], points: [ { points: 85, bonus: 20 }, { points: 85, bonus: 10 } ] }, { name: "Bob", age: 42, phone: { personal: "555-123-123", work: "555-456-456", ext: "7673" }, privileges: "admin", favorites: { artist: "Miro", food: "meringue" }, finished: [ 11, 25 ], badges: [ "green" ], points: [ { points: 85, bonus: 20 }, { points: 64, bonus: 12 } ] }, { name: "Willy", age: 22, phone: { personal: "555-123-123", work: "555-456-456", ext: "8263" }, privileges: "user", favorites: { artist: "Cassatt", food: "cake" }, finished: [ 6 ], badges: [ "blue", "Picasso" ], points: [ { points: 81, bonus: 8 }, { points: 55, bonus: 20 } ] }, { name: "John", age: 34, phone: { personal: "555-123-123", work: "555-456-456", ext: "2143" }, privileges: "admin", favorites: { artist: "Chagall", food: "chocolate" }, finished: [ 5, 11 ], badges: [ "Picasso", "black" ], points: [ { points: 53, bonus: 15 }, { points: 51, bonus: 15 } ] }, { name: "Steve", age: 23, phone: { personal: "555-123-123", work: "555-456-456", ext: "8253" }, privileges: "user", favorites: { artist: "Noguchi", food: "nougat" }, finished: [ 14, 6 ], badges: [ "orange" ], points: [ { points: 71, bonus: 20 } ] }, { name: "Martin", age: 43, phone: { personal: "555-123-123", work: "555-456-456", ext: "5623" }, privileges: "user", favorites: { food: "pizza", artist: "Picasso" }, finished: [ 18, 12 ], badges: [ "black", "blue" ], points: [ { points: 78, bonus: 8 }, { points: 57, bonus: 7 } ] } ``` Open your mongoshell, introduce the documents above in the collection `employees` and perform the following queries: - List all the employees - Find the employee with whose name is Steve - Find all employees whose age is greater than 30 - Find the employee whose extension is 2143 *(Should take approximately 10 minutes)* ## Update commands The update command allows us to update some fields in the document or replace all document: | Mongo Method | Description |--------------|--------------------- | [`db.collection.updateOne(<filter>, <update>)`](https://docs.mongodb.com/v3.2/reference/method/db.collection.updateOne/) | Updates a single document within the collection | [`db.collection.updateMany(<filter>, <update>)`](https://docs.mongodb.com/v3.2/reference/method/db.collection.updateMany/) | Updates multiple documents within the collection | [`db.collection.replaceOne(<filter>, <update>)`](https://docs.mongodb.com/v3.2/reference/method/db.collection.replaceOne/) | Replaces a single document within the collection All these methods accept two parameters: - `<filter>` finds what documents to update (same as [query filters](https://hackmd.io/mGLw7B34Q7a0GfnaaqfQmw#Query-Filters)) - `<update>` specifies what fields to update in the document :::info lecture Bien se référer à la [doc Mongo](https://docs.mongodb.com/v3.2/reference/method/db.collection.updateOne/). ::: ### updateOne() [db.collection.updateOne()](https://docs.mongodb.com/v3.2/reference/method/db.collection.updateOne/) updates a single document that matches the specified filter. :::warning :warning: If we have more than one document that matches that filter, this operation will update only one. ::: Following with the collection `employees`, let's update the user `"Martin"` with his new extension number. ```javascript db.employees.updateOne( { "name": "Martin"}, { $set: { "phone.ext": 1234 }} ) ``` This function will return an object where `"matchedCount"` is how many documents satisfy the filter and `"modifiedCount"` is how many documents were updated. :::warning :warning: Notice that when we need to access nested objects like the `ext` field inside `phone`, we need to use dot notation like JavaScript objects. They have to be in double quotes: `"phone.ext"`: ```javascript { name: "Martin", age: 43, phone: { personal: "555-123-123", work: "555-456-456", ext: "5623" }, privileges: "user", favorites: { food: "pizza", artist: "Picasso" }, finished: [ 18, 12 ], badges: [ "black", "blue" ], points: [ { points: 78, bonus: 8 }, { points: 57, bonus: 7 } ] } ``` ::: ```javascript { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 } ``` **Your turn!** Try to update the age of Sue to 20. ### updateMany() [db.collection.updateMany()](https://docs.mongodb.com/v3.2/reference/method/db.collection.updateMany/) updates all documents that match the specified filter. We want to update all employees older than 30 so that their favourite writer is Cervantes. ```javascript db.employees.updateMany( { "age": { $gte: 30 }}, { $set: { "favorites.writer": "Cervantes" }} ) ``` Result: ```javascript { "acknowledged" : true, "matchedCount" : 3, "modifiedCount" : 3 } ``` ### replaceOne() [db.collection.replaceOne()](https://docs.mongodb.com/v3.2/reference/method/db.collection.replaceOne/) replaces the first document that matches the specified filter even if there are more than one. Our employee `"Martin"` is not working anymore in our company so let's replace him for our new employee `"Susan"`. The new document for `"Susan"` ```javascript var susan = { "name" : "Susan", "age" : 25, "phone" : { "personal" : "555-223-223", "work" : "555-421-426", "ext" : 4240 }, "privileges" : "user", } db.employees.replaceOne( { "name": "Martin"}, susan ) ``` :::info :bulb: Replacing a document does not change its `_id` value. The `_id` field is immutable. If we include it in the new document it must have the same value as before. ::: ## Delete commands The delete commands remove documents from our collections. ### deleteOne() ```javascript db.employees.deleteOne( { "_id": ObjectId("583ea82c58613b64d5df8405") }) ``` ### deleteMany() If we want to remove **all** documents ```javascript db.employees.deleteMany({}) ``` Removes the documents that match the filter criteria. ```javascript db.employees.deleteMany({ age : { $gte: 30 }}) ``` ## Independent Practice Now that you know how to insert, delete, query and update documents, let's practice! - Find all employees that are over 30. - Find all employees that are less than or equal to 30. - Find all the employees whose favorite food is pizza. - Change Willy's personal phone number to "93-123-45-67". - Change Bob's privilege to normal user. - Find all employees whose favorite artist is equal to Picasso. - Delete the user John. - (EXTRA) Add a bonus of 15 to all those who have a bonus less than 10. ## Summary In this lesson we learnt how to perform basic operations with MongoShell. ## Extra Resources - [Mongo CRUD Documentation](https://docs.mongodb.com/v3.2/crud/) - [Query selectors in MongoDB](https://docs.mongodb.com/v3.2/reference/operator/query/#query-selectors) - [Reference Operators](https://docs.mongodb.com/v3.2/reference/)