Now that we have learnt about Mongoose Population,
and how it enables us to populate one document with information from another document.
In this exercise, we will modify the express server that we have been working on so far.
In the dish schema that we have defined earlier, we had comments.
For the comments, we had the author field
that we use to store the details about the author.
In this exercise, we will turn the author field into a reference to a user document,
and we will use Mongoose population to populate the information into
the dishes document as and when required in order
to supply the information to the client.
Now the use of populate and Mongoose population should be done
judiciously so as not to cause too much of overhead on the server side.
Now in this exercise,
we're going to use it simply to populate the information
into the author field of our comments.
So, let's proceed with the exercise to learn how we use Mongoose population.
To get started on this exercise,
go to the project and open the user.js file.
So, in the user.js file,
we store the user schema.
I'm going to modify the user schema by adding in a couple of more fields in there.
One is the first name,
which will be of
the type string and
the default would be an empty string.
So, the first name as the name implies,
stores the first name for the user and then we'll have another field called last name,
which is also of the same kind.
So, I'm just going to copy these two pieces of information and then copy it in
here and so now our user document will contain,
in addition to the username and password,
username and hush and salt that we have seen earlier,
that is automatically added by the passport local Mongoose module.
We will also have the first name and last name for the user being defined here.
Later on we will see how we would initialize
these values by modifying the user's registration process.
Now once we have completed this,
so this way the user's information
can simply be retrieved by looking up the user document here.
So, now that we have the information about the user in the user document,
going into the dish schema,
so going into dishes.js file.
In the dish schema earlier,
we were storing the author of the document in the form of a string here.
Now we're going to be taking advantage of the fact that we
have the support of mongoose population.
So I'm going to turn the comment field from a string into
mongoose schema types object ID.
So, this way, sorry, wrong field.
I meant to turn the author field into
mongoose schema types object ID.
So, the author field now instead of storing a string,
will have a reference to the user document.
So, when I turn the author field into this type,
then the second property that I defined here will be a reference,
which would be a reference to the user model.
So, this way, we are now going to be
connecting this author field and this author field will
simply store a reference to the ID of the user document,
instead of storing the details about the author in the form of a name.
Now when we do that,
we can use mongoose populate to populate
this information into our dishes document whenever required.
So, with this modification to the dishes schema, in dishes.js file,
we'll now update the dish router to use the mongoose population.
So, going to dishRouter.js.
In dish router, recall that when we were getting a dish here,
now when you get the dish here,
we say dishes find then.
So, right at that point,
we'll say dishes find and we will say after this, populate.
So, we are using the population support in mongoose
and we'll say populate comments author.
So, by stating this,
we are saying when
the dishes document has been constructed to send back the reply to the user,
we're going to populate the author field inside there from the user document in there.
So, this call to the populate will ensure that
the other field will be populated with the information as required.
Similarly, going to the dish ID here,
even in the dish ID, same thing.
We will say populate and comments author added
into the dishes find by id in
the get of the /dish ID endpoint.
Similarly, in the comments also when we retrieve the dish,
we'll say populate comments author here and the
same thing also in the dish router,
dish ID comments, comment ID also.
The bigger populating this information there.
Now of course, what this means is that when you are posting the dish,
earlier you are including the author information in the body of the message.
So, now here when we try to push the comment into that,
so this post corresponds to the dish ID comments field.
So, this is how we were posting a comment to a specific dish.
So, now in this post,
since we are not storing the information about the author anymore,
so what we need to do is when we push the item into the author field there.
So, here when you're populating the information into the dish,
we will have to first-
Recall that the body contains the comment already,
but the author property will not be there in the body of the message in the book,
but depending on which user is posting this information,
we can immediately populate the author field.
Now, how do we know which user is posting this information?
The fact that we have done the verify user here for the post,
means that a specific user is posting this information,
and by doing the verify user,
we would have already loaded in the req.user into the request object.
In the request object,
we can go in and say wreck user,
and then underscore ID here.
So, again let me reiterate this point,
how are we obtaining the author's information here?
Now, recall that we updated the dishes schema,
so that the author field in the comment will simply store
the object ID referring to the user that is posting this comment.
Now, how do we know which user is posting this comment?
Now again, to reiterate this point,
recall that when we verified the user here by calling authenticate verify user,
the passport authorized JWT would have loaded
the user information into the request body in the form of req.user.
So, that user will contain the ID of
the specific user that is actually posting this comment.
So, we have already verified the authenticity of the user,
and so the user ID can simply be obtained by saying req.user.
_ID, and that user's ID,
I will assign this to the author field off the comment.
Now when the comment comes in,
the comment in the body of the request message will
only contain the rating field and the comment field.
Now we don't want to explicitly send the author field anymore from the client side,
instead that should be automatically inserted
on the server side based upon the authenticity of
the user That's the point that I had been
reiterating in this modification that I have done here.
So, that users information is automatically obtained from the req.user that is
loaded into the body of the request message by the authenticate verify user,
which is going to use Passport authenticate with the JWT strategy there.
In addition, now when we receive the updated dish here,
we need to populate the author's information into the dish.
So, at this point,
when we receive the dish here,
we are then going to search the dishes here.
So, we'll say Dishes.findByID
and then supply the dish ID as the parameter here,
so we'll say find by ID,
dish ID, and then,
we need to populate the comments author here,
and then we'll say then dish.
Inside there, we're going to be sending this dish information back to the user here.
So, let me cut that and paste that in here.
So, this modification is required because now I need to populate
the author's information back into
the comment before I can send the current back to the user.
So, this is the additional modification that we need to
do when we use the Mongoose population here.
Similarly, now going into the put,
when we modify a specific comment with the comment ID,
so this is under the dish ID comments comment ID part.
So, when we do the put here,
so we first find the dishes find by id req params dish ID,
then in the dish.
So, the first thing that we check is to ensure that if the dish is not null,
and the dish comments ID is not null,
so we checked to make sure that the comment is indeed present in the dish,
and then when the dish itself is returned,
then we need to search again for
the dish because we need to populate the comments author into the dish.
So here, we'll say dishes.findByID (dish ID),
the reason we need to do one more search is because
we need to populate the comments.author here,
so that's the only reason why we need to do one more search here.
Then when we receive the dish here,
obviously because we just updated the dish so the dish information should be
found in the database,
so that should work fine and then inside there will say
risk status code 200 res set header content type application json,
and then return the dish here,
and then we'll handle error here,
and then the other ones if the dishes
now and also the other errors that we have set up earlier,
they will be handled as usual here.
So, these are the additional changes that we need to make sure when you update the dish,
when you're sending back the updated comment or updated dish,
then we will populate the comment in the dish here.
Similarly, going to the delete here,
and then after the delete the comment,
again we're going to be fetching the dish and populating the author information.
So, let me just copy this part,
and then we'll be doing exactly the same thing here,
so we'll say dish save,
then we're going to be then checking dish.findByID (dish author),
and then we'll populate the comments author,
and then we'll say (then) dish,
and then res.statusCode, and so on,
and the remaining error handling just like before here.
So, with this modification to the dish router,
now the last point that we need to consider is the fact that in the user.js file,
we have now added into fields,
the first name and last name field which by default will be empty strings.
So, when the user is registering,
we should allow the user to supply the first name and
last name in the registration process.
Now, where does that take place?
That takes place in the user's.js.
So, going to users users.js,
when the user posts on the slash signup,
earlier we were only posting the username and password.
In addition to those two,
in the json object that we include in the body of the request message coming in,
the post request message coming in from the client side,
we can also include the first name and last name for the user.
So, when the first name and last name for the user is included,
so what am I going to do here?
So, recall that when you say user.register at this point the user information comes in,
and then you have submitted the username here,
and you have also assigned the password here which will be
turned into the hash and salt by the passport local mongoose.
Now, if there is no error that means that the user's registration was successful,
and so at this point what we will do is we'll say if req.body.
First name. So, which means that the body of the incoming request message,
if it contains the first name,
then we'll say user.firstname is equal to req.body.firstname.
Similarly, for the last name also.
So at this point,
we would have the user available here.
See the user is coming in as the second parameter to this callback function here.
So, we are setting up the first name by changing
the first name property inside the user document here saying, req.body.firstname.
If it exists, then we'll set the user's first name to that.
Similarly, if the req.body.last name is available,
so we'll also update the user's last name as req.body.lastname.
And once we have done these two changes to the first name and the last name,
then we need to save the modification that we have done to the user.
So, we have just updated the user.
So, we'll say user.save then this will
return the error or the user.
So, if the modification has been saved properly,
then it will return the error,
otherwise it will return the user value and this passport authenticate we will
do it inside this user here.
So, we'll say, user.save(err, user).
And then also we need to cross check to make sure that
if there is an error in saving the changes to the user,
then we'll say res status code 500,
so let me copy this from there.
So, we'll say res status code 500,
res set header content type application json and res.jason here.
Then, and we'll return to this point.
If there is no error,
then of course you authenticate the user by calling
passport authenticate with the local to ensure that
the user registration has been successful and this should be
correctly done and when which case you will return this message back to the client side.
We need to close this user.save here.
So, make sure that you close this endpoint correctly.
So, user.save is closed here, and that's it.
These are the changes that we need to make to the user.
So thereby, after the user is registered with the given username and the given password,
then after the user is successfully registered,
then we will set the first name and last name field
of the user document by using these two here.
We want to make sure that the user is successfully
registered before we sent the first name and last name for that.
So, that's why we are carrying out this operation after the user
is successfully registered. That's it.
Let's save the changes and go and check out the server.
After saving the changes,
let's now go to the terminal and
then before I start the server,
let me first check out my MongoDB and delete the user that we have registered earlier.
So, we'll say use confusion and then we'll say db.usersfind.
So, we know that this particular user has been registered earlier,
but when we register that user,
we did not register the first name and last name for the user.
So, I'm going to delete this user and then re- register the user.
So, to do that using the Mongo ripple,I will say db users drop,
and then we'll say db users find,
and that should return an empty.
No users registered there and then we'll exit the Mongo ripple.
And so, once we have removed that registered user,
then, let me start my server.
And once the server is up and running,
let's go to Postman and then register
a new user along with the first name and last name of the user.
Then, they will log in as that user and then we'll look at how
the Mongoose population helps us to populate the information about
the user automatically into the document there.
Now going to Postman,
let me do a sign up of a new user.
So, I'm doing a post localhost: 3000 users sign up.
In the body of the message,
we had the username and the password already in there.
Let me add in two additional fields:
first name, last name.
Then, register that user.
So, once I register the user,
you can see that the registration was successful.
Now, let me login as this user.
So, to log in as the user,
let me do a post and cross check to make sure.
So, I'm doing a post to users login.
Let me cross check and I see that the username and password is correctly typed in there.
So when I log in,
I should be successfully logged in and I should be able to obtain this token there.
Because this token is essential for us to be able to add in a dish to our server site.
So, after you obtain the token,
copy this token string and save it because you will need that in
the authorization header for the post
put and delete operations that you're going to perform later.
So let me copy that token.
Now, normally the way I would keep these tokens is,
that I'll just open a text document and then copy and paste it into the text document.
So that for subsequent postman requests,
I can simply copy this string and then paste it into
the authorization header, if required.
So, let me copy that token and here I have a text document open here.
So, I'm going to paste that string into this text document.
So, here we have the token that we have obtained.
Let's now go and post a dish to our server.
Going back to postman,
let me post a dish to the server.
So, this is where I will choose the post here.
Inside here, I have the dish information that I had used earlier but for the comments,
now, recall that earlier we had author field which was storing a string.
So, all these comments are not valid.
So, I'm going to delete all these comments from submission because,
now, we expect the user to post comments on their own.
When the user posts comments,
we will automatically add in the ID of the user into the author field of the comments.
So, let me post this dish here.
Going to the header,
in the authorization header, I'm going to say,
bearer and then, paste the token and then submit.
I should do a post on that.
So, I will say post and so when I post now,
you see that this dish has been posted on the server side,
and with the comments array being empty at this moment.
So, after I post this dish,
let me copy the ID of this dish.
So, let me copy this ID for the dish because I will
need that to post comments for this dish.
Then, going to my text editor,
I'm going to save that ID of the dish here.
Now, of course, once you build your client side,
your client will automatically have all this information.
So, your client will automatically be able to send the token and so on.
So, you don't need to do this cut-and-paste thing but with postman,
this is the only way that we can add any information to our postman requests,
that go out from postman to the server.
Now, in order to convince ourselves that this dish actually exists,
let me do a get on the local hostcolon:3000/dishes.
When I do a get, you can actually see that
this particular dish exists on the server side.
So, let's now try to post a comment.
So, to post a comment,
let's do a post and we'll say,
localhost:3000/dishes, slash and the ID of the dish that
I just copied, and slash comments.
When you post on the comments,
you need to make sure that in the body we will add in the comment here.
So, a typical comment contains
a rating of say
five and then comment.
So, let me just type in some random comment,
just to demonstrate to you.
So, this should be in the body of the post for the comments and in the header,
we should add the authorization header.
So, for the authorization header, we'll say, bearer.
I need to paste the token here,
pasting the token value that I have saved earlier.
Let's now post this comment.
Then, when the comment is posted,
let's look at the returned value here.
So, as you browse down,
you can see that the dish to which the comment has been added, has been returned.
Notice that the dish information is there but note in particular,
what is contained in the comment that has been posted here.
So, as you can see, you already know that the updated and
created at fields are automatically added in by mongoose.
The rating and the comment that we submitted are
right there but note how the author field
now contains the ID corresponding to the user.
Now, as we saw in the code how the author field information is added, now,
if you do a get on the dishes,
you will notice that this author field will be
automatically filled in by the users information here.
So, let's now do a get on the localhost:3000/dishes.
So, when we now do a get on this point,
you'll now notice that in the dishes here,
right there, the information about the dish is already
present but note how the comment is now constructed.
The comment now contains,
the rating comment fields as we saw earlier,
the updated and created at,
but note what happened to the author field here.
So, when you do a get request because we did a populate on
the server side when the get operation is invoked,
the populate has automatically populated
the author information into position in the author field here.
So, in there, you can see that from the author,
you can now look up the last name and
the first name information automatically from the author field.
So, if you need to construct a comment,
you now have the rating,
the comment, and also the first name and last name of
the author automatically included into this document.
Also, the user name also is included into this document.
So, this is how you can add in information from another document and populate
a second document with that information before you reply back from the server site.
So, this is the use of mongoose population and how we
can automatically populate information into a mongoose document.
With this, we complete this exercise.
In this exercise, we have seen the use of mongoose population and we have also seen
how we can populate information from one document into another document.
Whereby, when we modify the server to do the population for the requests,
mongoose will automatically take care of populating this information for us.
All that we need to do,
is to store the reference to the other document in the form of the object ID,
in the document into which you want to populate this information.
With this, we complete this exercise.
This is a good time for you to do a git-commit with the message, mongoose population.