Updating data
You know how to insert, delete, and fetch documents out of MongoDB. In this section, you're going to learn how to update documents in your MongoDB collections. To kick things off, as usual, we're going to duplicate the last script we wrote, and we'll update it for this section.
I'm going to duplicate the mongodb-delete file, renaming it to mongodb-update.js, and this is where we'll write our update statements. I'm also going to delete all of the statements we wrote, which is the deleted data. Now that we have this in place, we can explore the one method we'll be looking at in this section. This one is called findOneAndUpdate. It's kind of similar to findOneAndDelete. It lets us update an item and get the new document back. So if I update a Todo, set it as completed equal to true, I will get that document back in the response. Now in order to get started, we're going to be updating one of the items that we have inside of our Todos collection. If I view the documents, we currently have two. The goal here is going to be to update the second item, the one where text equals Eat lunch. We're going to try to set the completed value to true, which would be a pretty common action.
If I check off a Todo item, we want to toggle that completed Boolean value. Back inside of Atom, we're going to kick things off by accessing the appropriate collection. That'll be db.collection. The collection name is Todos, and the method we'll be using is findOneAndUpdate. Now, findOneAndUpdate is going to take the most arguments we've used so far, so let's go ahead and look up the documentation for it for future reference.
Over inside of Chrome, we currently have the Cursor tab open. This is where we have the count method defined. If we scroll next the Cursor tab, we have our other tabs. The one we're looking for is Collection. Now, inside of the Collection section, we have our typedefs and our methods. We're looking at methods here, so if I scroll down, we should be able to find findOneAndUpdate and click it. Now, findOneAndUpdate takes quite a few arguments. The first one is the filter. The update argument lets us target the document we want to update. Maybe we have the text, or most likely we have the ID of the document. Next up is the actual updates we want to make. We don't want to update the ID, we just want to filter by ID. In this case, the updates are going to be updating the completed Boolean. Then we have some options, which we are going to define. We'll use just one of them. We also have our callback. We're going to leave off the callback as we've been doing so so far, in favor of promises. As you can see on the documentation page, it returns a promise if no callback is passed in, and that's exactly what we expect. Let's go ahead and start filling out the appropriate arguments for findOneAndUpdate, kicking things off with the filter. What I'm going to do is filter by ID. In Robomongo, I can grab the ID of this document. I'm going to edit it and copy the ID to the clipboard. Now, in Atom, we can start querying the first object, filter. We're only looking for documents where the _id equals new ObjectID with the value that we copied to the clipboard. This is all we need for the filter argument. Next up is going to be the actual updates we want to apply, and this is not exactly straightforward. What we have to do here is learn about the MongoDB update operators.
We can view a complete list of these operators and exactly what they are by googling mongodb update operators. When I do this, we're looking for the mongodb.com documentation:
Now this documentation is specific to MongoDB, which means it's going to work with all of the drivers. In this case, it is going to work with our Node.js driver. If we scroll down further, we can look at all of the update operators we have access to. The most important, and the one we're going to get started with, is the $set operator. This lets us set a field's value inside of our update, which is exactly what we want to do. There's other operators, like increment. This one, $inc, lets you increment a field's value, like the age field in our Users collection. Although these are super useful, we're going to get started with $set. In order to use one of these operators, what we need to do is type it out, $set, and then set it equal to an object. In this object, these are the things that we're actually going to be setting. For example, we want to set completed equal to true. If we tried to put completed equal to true at the root of the object like this, it would not work as expected. We have to use these update operators, which means we need this. Now that we have our updates in place using the set update operator, we can go ahead and provide our third and final argument. If you head over to the documentation for findOneAndUpdate, we can take a look at the options real quick. The one we care about is returnOriginal.
The returnOriginal method is defaulted to true, which means that it returns the original document, not the updated one, and we don't want that. When we update a document, we want to get back that updated document. What we're going to do is set returnOriginal to false, and that's going to happen in our third and final argument. This one is also going to be an object, returnOriginal, which is going to be setting equal to false.
With this in place, we are done. We can tack on a then call to do something with the results. I'll get my result back and I can simply print it to the screen, and we can take a look at exactly what comes back:
db.collection('Todos').findOneAndUpdate({ _id: new ObjectID('5a86c378baa6685dd161da6e') }, { $set: { completed:true } }, { returnOriginal: false }).then((result) => { console.log(result); });
Now, let's go ahead and run this from the Terminal. I'm going to save my file inside the Terminal. We're going to be running node. The file is in the playground folder, and we will call it mongodb-update.js. I'm going to run the following script:
node playground/mongodb-update.js
We get back the value prop, just like we did when we used findOneAndDelete, and this has our document with the completed value set to true, which is the brand-new value we just set, which is fantastic:
If we head over to Robomongo, we can confirm that the value was indeed updated. We can see this in the old document, where the value is false. I'm going to open up a new view for Todos:
We have Eat lunch, with a completed value of true. Now that we have this in place, we know how to insert, delete, update, and read documents from our MongoDB collections. To wrap this section up, I want to give you a quick challenge. Over inside of the Users collection, you should have a document. It should have a name. It's probably not Jen; it's probably something that you set. What I want you to do is update this name to your name. Now if it's already your name, that's fine; you can change it to something else. I also want you to use $inc, the increment operator that we talked about, to increment this by 1. Now I'm not going to tell you exactly how increment works. What I want you to do is head over to the docs, click on the operator, and then scroll down to see the examples. There's examples for each operator. It's going to become really useful for you to learn how to read documentation. Now, documentation for libraries is not always going to be the same; everyone does it a little differently; but once you learn how to read the docs for one library, it gets a lot easier to read the docs for others, and I can only teach so much in this course. The real goal of this course is to get you writing your own code, doing your own research, and looking up your own documentation, so your goal once again is to update this document, setting the name to something other than what it's currently set to, and incrementing the age by 1.
To kick things off, I'm going to grab the ID of the document in Robomongo, since this is the document I want to update. I'll copy the ID to the clipboard, and now we can focus on writing that statement in Atom. First up, we'll update the name, since we already know how to do that. In Atom, I'm going to go ahead and duplicate the statement:
db.collection('Todos').findOneAndUpdate({ _id: new ObjectID('57bc4b15b3b6a3801d8c47a2') }, { $set: { completed:true } }, { returnOriginal: false }).then((result) => { console.log(result); });
I'll copy it and paste it. Back inside of Atom, we can start swapping things out. First up, we're going to swap out the old ID for the new one, and we're going to change what we passed to set. Instead of updating completed, we want to update name. I'm going to set the name equal to something other than Jen. I'm going to go ahead and use my name, Andrew. Now, we are going to keep returnOriginal set to false. We want to get the new document back, not the original. Now, the other thing that we need to do is increment the age. This is going to be done via the increment operator, which you should have explored using the documentation over inside of Chrome. If you click on $inc, it's going to bring you to the $inc part of the documentation, and if you scroll down, you should be able to see an example. Right here, we have an example of what it looks like to increment:
We set $inc just like we set set. Then, inside of the object, we specify the things we want to increment, and the degree to which we want to increment them. It could be -2, or in our case, it would be positive, 1. In Atom, we can implement this, as shown in the following code:
db.collection('Users').findOneAndUpdate({ _id: new ObjectID('57abbcf4fd13a094e481cf2c') }, { $set: { name: 'Andrew' }, $inc: { age: 1 } }, { returnOriginal: false }).then((result) => { console.log(result); });
I'll set $inc equal to an object, and in there, we'll increment the age by 1. With this in place, we are now done. Before I run this file, I am going to comment out to the other call to findOneAndUpdate, just leaving the new one. I also need to swap out the collection. We're no longer updating the Todos collection; we're updating the Users collection. Now, we are good to go. We're setting the name equal to Andrew and we're incrementing the age by 1, which means that we would expect the age in Robomongo to be 26 instead of 25. Let's go ahead and run this by restarting the script over in the Terminal:
We can see our new document, where the name is indeed Andrew and the age is indeed 26, and this is fantastic. Now that you know how to use the increment operator, you can also go off and learn all of the other operators you have available to you inside of your update calls. I can double-check that everything worked as expected in Robomongo. I'm going to go ahead and refresh the Users collection:
We have our updated document right here. Well, let's wrap this section up by committing our changes. In the Terminal, I'm going to run git status so we can view all of the changes to the repository:
Here, we just have one untracked file, our mongodb-update script. I'm going to use git add . to add that to the next commit, and then I'll use git commit to actually make the commit. I am going to provide the -m argument for message so we can specify a message, which is going to be Add update script:
git add . git commit -m 'Add update script'
And now we can run the commit command and push it up to GitHub, so our code is backed up on our GitHub repository:
git push
With updating in place, we now have all of the basic CRUD (Creating, Reading, Updating, and Deleting) operations down. Up next, we're going to talk about something called Mongoose, which we'll be using for the Todo API.