In the previous lesson,
we learned about the MongoDB driver.
That enables our node application to communicate with a MongoDB server,
and also store and retrieve documents from the MongoDB server.
We also saw that the MongoDB driver provides us with
many methods that enable us to create collections within a database,
add documents to the collections,
and then perform various operations on the documents within a collection.
Now, when the documents are stored in the database,
the MongoDB driver itself imposes no structure on the documents.
If we need to have specific structure for the documents and enforce that structure,
then we need to make use of the Mongoose node module which enables us to define
a schema and a structure for our documents that are stored in our MongoDB database,
and strictly enforces the structure.
Let's look at more details in this lecture and the exercises that follow this lecture.
As we have already learned,
MongoDB stores data in collections in a database.
These collections consists of a collection of documents.
The documents themselves stored in
a MongoDB database have no specific structure imposed on the document.
Any document can be stored in any collection.
MongoDB relies on the developer to enforce the structure on the documents,
and gives the complete responsibility to the developer to make sure that documents
of the correct structure are added and maintained in the various collections.
Now, it is very easy to violate this.
So for example, even though you might start out with the assumption
that a particular connection will have documents of a certain structure,
you can easily insert documents that don't necessarily comply with the structure.
If you are very particular that the structure of the documents in
a collection always have a specified structure,
and always will have the specific set of fields,
then the MongoDB itself doesn't impose that neither
does the node MongoDB driver that we have seen in the previous lesson.
This is where we will need a more formal way of imposing
structure on the documents that are stored in a collection in a MongoDB database.
This is where the Mongoose node module comes to our help.
The Mongoose node module imposes
a standardized structure for the documents that are stored in a particular collection.
So, that is why we often hear people referring to this as the Mongoose ODM.
The ODM itself is interpreted by some people to mean
Object Data Model or sometimes referred to as Object Document Mapping,
or some people refer to it as ORM or Object Relational Mapping.
Now, when we talk about relational that applies a lot more to relational databases,
but then with SQL databases we needed explicitly the object to
relational mapping to be put in between the database and our application itself.
Because within the application we would be looking at
objects but their storage in an SQL database will be in the form of records,
and so you need an explicit mapping.
As we saw with the NoSQL database,
this was not explicitly required.
But if you need to impose structure on your documents that are stored in a collection
then the use of Mongoose to impose this structure is very, very useful.
The way Mongoose goes around
imposing structure on the documents is through the use of schema.
Schema, defines the structure of their documents.
Let's talk about that in a bit more detail.
So, what exactly is mongoose schema,
and what does it bring to the table?
Mongoose schema, implies a structure on
the data that is stored in a document in your database.
So, it defines all the fields of your document,
and also specifies the types of the fields,
and also can provide us with
additional features that can enable validation on these fields.
So, for example, the various schema types that are supported in Mongoose include: String,
Number, Date, Buffer, Boolean,
Mixed, object ID, and Array.
In particular we will look at string number,
and a date, and Boolean in the exercise that follows.
We will look at some of the other ones in later exercises.
In particular, notice the use of the array schema type.
So, an array schema type would allow you to
create an array of sub-documents inside the document.
I'll talk about that in a short while.
Once you define a schema,
the schema is then used in Mongoose to create a model function,
and that is what enables you to define the structure for your documents in the database.
Schemas themselves can have nesting.
So, which means that you can have sub-document that are enclosed inside a document.
The sub-documents typically are accommodated
either through specifying an additional schema,
and then defining one of the fields of the schema to be off the type of the other schema.
Or you can even go with an array of
another schema type within a second schema that you define.
Let's look at an example to clarify some of these in more detail.
This example will be from the exercise that you will do right after this lecture.
Before I can talk about schemas and models in Mongoose,
let's understand why we would need that.
If you have taken the previous angular,
or the ionic, or the native script course,
you have seen that we represent
various data that we use in our applications in the form of JSON strings.
So, in our application we define a collection called as dishes.
In a dish collection,
each dish will contain a certain set of properties defined in the form of JSON string,
as you see in this example here.
So, the dishes is an array of dish type,
and each dish itself will contain a name,
an image, a category,
a label, and so on.
Also, inside the dish document itself,
you would have comments which are stored as an array of again,-
JSON documents which contain specific fields step.
So, each comment, for example,
contains a rating comment author and a date field as you see in this example here.
So, you see that there is a clear structure to
each document that defines a dish that is stored in our database.
Multiple dishes obviously will be stored in the form of a collection in our database,
and could be grouped together and sent over as an array of
dishes to our client application to be made use of.
Now that we have understood how this is defined, now,
how does this relate to the Mongoose schema and the model that we define in Mongoose?
Now, note the structure of a typical dish document here.
So, this could be easily mapped into a MongoDB document in a collection,
perhaps named the dishes collection.
So, we see that there is a clear structure to the document itself.
Now, how do we mirror this in a schema in our Mongoose application?
As you will learn in the exercise,
we will see that we would define schemas in Mongoose.
So the schema is defined as a Mongoose schema here.
As an example, a commentSchema is shown here.
The commentSchema, as you can see,
contains three different fields: the rating, comment,
and the author field,
and also timestamps here.
The timestamps allow you to have two different fields in
the document: the created at field and the updated at field,
both of which are timestamps stored in the form of an ISO date string in the document.
Now, the rating itself would be an integer value.
So, in Mongoose terminology,
it will be stored as a number,
the type would be a number.
You can even specify the minimum and the maximum value.
You can also specify that this particular field is required, so,
which means that every document of the comment type should contain a rating field.
Similarly, you can also define a comment field,
which is of the type string.
So, obviously, a comment is nothing but a string which contains some information,
and this can also be defined as a required field,
meaning that every document should contain a comment,
and also an author field,
which is also of the type string.
So, you see that by defining this schema in this format.
As we learned in the discussion a little bit earlier,
schema is defined by using the various types that we have in our Mongoose application.
So, in the schema, again,
you see three different fields here,
rating, comment, and author here,
and each of which has a specific type given,
and then whether this is required or not.
So, thereby, you are imposing a strict structure
on the comment documents that are going to be storing in your application.
Now that we have defined a comment schema, we can then,
as you noticed from the example of the kind of data that we require in our application,
we have a dish document itself.
The dish document contains various fields.
Here, in the exercise,
we will first introduce just two fields into the dish document,
the name, and the description.
In the next lesson,
we will introduce the remaining fields for the dishSchema.
Now, so the name,
as in this case,
is of the type string.
We can also specify that this is a required field,
meaning that every document should contain the name field.
We can also specify that the name field is unique,
meaning that no two documents can have exactly the same name value in the document.
So, that ensures that each document will have
a unique name field in it and a description field,
which is again of the type string,
but also specified as required.
Now, as we saw in the example,
a dish document contains multiple comments enclosed inside the document.
Now, in Mongoose, this is supported through the use of sub-documents.
So, if you define a schema earlier,
so for example, we have already defined a commentSchema here,
you can also define a field into another schema that you define and
then specify that that field will be of
the type of the previous schema that you have defined.
So, in this case,
the comments field, I am defining it as an array,
so you see the use of an array type in your schema that you're defining here,
and then array of the commentSchema type.
So, this is an array of comments that will be included in each dish document.
So, thereby, you can have
more than one comment sub-document enclosed inside a dish document.
So, defining the structure like this enables us to support
the corresponding JSON string structure that we have defined
for a dish document or that we have seen in the example earlier.
Now, once we define the schema,
to make use of this in our application,
we need to create a model from that schema that we have just defined.
So, within our application,
we will define a Mongoose model and specify that the model
is off the type dishSchema in this example.
Not only that, you will also give a name to the model here.
So, when you give a name to the model here,
we are specifying the name as dish.
Now, when you use this dish model in
our node application where we are making use of Mongoose,
then this will be transformed and mapped into a collection in my MongoDB database.
The collection itself will be named as dishes.
So, Mongoose automatically knows that when you specify a name here,
it'll automatically construct the plural
of that name and then give the collection the name,
which is the plural of the model name that you specify in this example here.
So, when I say dish here,
then Mongoose automatically will map this into
the dishes collection in the MongoDB database.
How does it know how to convert this singular name to a plural?
Mongoose automatically has a built-in mechanism that
enables it to construct the plurals of standard English words.
So, if you say dish,
it will construct dishes.
If you say leader, it will construct the plural of it as leaders, and so on.
So, this is already built into the Mongoose node module.
So, that is why when I specify this as a dish model type,
then Mongoose will construct the dishes collection in my MongoDB database,
and then that dishes collection will store
the various documents of the dish type in there.
Once we have created that, typically,
when we declare models in our application,
we would store them in a sub-folder named models, just for convenience.
You don't need to do that,
but just to organize your application,
we would normally store that in a folder named models.
Then the schema and the model would be
defined in a file like this as you see in the example here,
this called dishes.js, and then this would be exported because this is a node module.
This'll be exported from this file so that it can be included into the node application,
where we are going to make use of
this schema and the model that we have just defined here.
With this quick understanding of schema and model and
its use in defining the structure for a document that we store in a MongoDB,
let's go back and understand a little more about what Mongoose provides for us.
In addition, Mongoose enables us to establish the connection with the MongoDB server.
Mongoose internally make use of
the MongoDB driver that we had used in the previous exercise.
So, Mongoose depends upon the MongoDB driver, so,
which means that from your Mongoose-based node application,
you can use all the methods that are already
available from the MongoDB driver also if you choose to,
but Mongoose itself has its own collection of methods that we
can make use of to interact with the MongoDB database,
as we will see in the exercise that follows.
Let me briefly show you how we would establish a connection to the database,
and you will do this in the exercise that follows.
So, just like we declared the URL in case
of the MongoDB node application in the earlier lesson,
we will still declare the URL for our application.
Then we will use the Mongoose connect method
and supply the URL for the Mongoose connect method,
and this will establish the connection to the database.
With this quick understanding of Mongoose and what role Mongoose
plays in supporting structured insertion,
storage, and retrieval of documents from our MongoDB,
let's move on to the exercise where we will
get some hands-on experience using the Mongoose module.