by Nick Karnik
Mongoose is an Object Data Modeling (ODM) library for MongoDB and Node.js. It manages relationships between data, provides schema validation, and is used to translate between objects in code and the representation of those objects in MongoDB.
You can think of a Mongoose schema as the configuration object for a Mongoose model. The following are all the valid SchemaTypes in Mongoose. The `type` Key.
MongoDB is a schema-less NoSQL document database. It means you can store JSON documents in it, and the structure of these documents can vary as it is not enforced like SQL databases. This is one of the advantages of using NoSQL as it speeds up application development and reduces the complexity of deployments.
Below is an example of how data is stored in Mongo vs. SQL Database:
‘Collections’ in Mongo are equivalent to tables in relational databases. They can hold multiple JSON documents.
‘Documents’ are equivalent to records or rows of data in SQL. While a SQL row can reference data in other tables, Mongo documents usually combine that in a document.
‘Fields’ or attributes are similar to columns in a SQL table.
While Mongo is schema-less, SQL defines a schema via the table definition. A Mongoose ‘schema’ is a document data structure (or shape of the document) that is enforced via the application layer.
‘Models’ are higher-order constructors that take a schema and create an instance of a document equivalent to records in a relational database.
Before we get started, let’s setup Mongo. You can choose from one of the following options (we are using option #1 for this article):
Let’s navigate through some of the basics of Mongoose by implementing a model that represents data for a simplified address book.
I am using Visual Studio Code, Node 8.9, and NPM 5.6. Fire up your favorite IDE, create a blank project, and let’s get started! We will be using the limited ES6 syntax in Node, so we won’t be configuring Babel.
Let’s go to the project folder and initialize our project
Let’s install Mongoose and a validation library with the following command:
The above install command will install the latest version of the libraries. The Mongoose syntax in this article is specific to Mongoose v5 and beyond.
Create a file ./src/database.js
under the project root.
Next, we will add a simple class with a method that connects to the database.
Your connection string will vary based on your installation.
The require(‘mongoose’)
call above returns a Singleton object. It means that the first time you call require(‘mongoose’)
, it is creating an instance of the Mongoose class and returning it. On subsequent calls, it will return the same instance that was created and returned to you the first time because of how module import/export works in ES6.
Similarly, we have turned our Database class into a singleton by returning an instance of the class in the module.exports
statement because we only need a single connection to the database.
ES6 makes it very easy for us to create a singleton (single instance) pattern because of how the module loader works by caching the response of a previously imported file.
A Mongoose model is a wrapper on the Mongoose schema. A Mongoose schema defines the structure of the document, default values, validators, etc., whereas a Mongoose model provides an interface to the database for creating, querying, updating, deleting records, etc.
Creating a Mongoose model comprises primarily of three parts:
This reference will be the same as the one that was returned when we connected to the database, which means the schema and model definitions will not need to explicitly connect to the database.
A schema defines document properties through an object where the key name corresponds to the property name in the collection.
Here we define a property called email with a schema type String which maps to an internal validator that will be triggered when the model is saved to the database. It will fail if the data type of the value is not a string type.
The following Schema Types are permitted:
Mixed and ObjectId are defined under require(‘mongoose’).Schema.Types
. Norton internet security 2018 product key generator.
We need to call the model constructor on the Mongoose instance and pass it the name of the collection and a reference to the schema definition.
Let’s combine the above code into ./src/models/email.js
to define the contents of a basic email model:
A schema definition should be simple, but its complexity is usually based on application requirements. Schemas can be reused and they can contain several child-schemas too. In the example above, the value of the email property is a simple value type. However, it can also be an object type with additional properties on it.
We can create an instance of the model we defined above and populate it using the following syntax:
Let’s enhance the Email schema to make the email property a unique, required field and convert the value to lowercase before saving it. We can also add a validation function that will ensure that the value is a valid email address. We will reference and use the validator library installed earlier.
Mongoose has a flexible API and provides many ways to accomplish a task. We will not focus on the variations because that is out of scope for this article, but remember that most of the operations can be done in more than one way either syntactically or via the application architecture.
Let’s create an instance of the email model and save it to the database:
The result is a document that is returned upon a successful save:
The following fields are returned (internal fields are prefixed with an underscore):
_id
field is auto-generated by Mongo and is a primary key of the collection. Its value is a unique identifier for the document.email
field is returned. Notice that it is lower-cased because we specified the lowercase:true
attribute in the schema.__v
is the versionKey property set on each document when first created by Mongoose. Its value contains the internal revision of the document.If you try to repeat the save operation above, you will get an error because we have specified that the email field should be unique.
Let’s try to retrieve the record we saved to the database earlier. The model class exposes several static and instance methods to perform operations on the database. We will now try to find the record that we created previously using the find method and pass the email as the search term.
The document returned will be similar to what was displayed when we created the record:
Let’s modify the record above by changing the email address and adding another field to it, all in a single operation. For performance reasons, Mongoose won’t return the updated document so we need to pass an additional parameter to ask for it:
The document returned will contain the updated email:
We will use the findOneAndRemove
call to delete a record. It returns the original document that was removed:
We have looked at some of the basic functionality above known as CRUD (Create, Read, Update, Delete) operations, but Mongoose also provides the ability to configure several types of helper methods and properties. These can be used to further simplify working with data.
Let’s create a user schema in ./src/models/user.js
with the fieldsfirstName
and lastName
:
A virtual property is not persisted to the database. We can add it to our schema as a helper to get and set values.
Let’s create a virtual property called fullName
which can be used to set values on firstName
and lastName
and retrieve them as a combined value when read:
Callbacks for get and set must use the function keyword as we need to access the model via the this
keyword. Using fat arrow functions will change what this
refers to.
Now, we can set firstName
and lastName
by assigning a value to fullName
:
The code above will output the following:
We can create custom helper methods on the schema and access them via the model instance. These methods will have access to the model object and they can be used quite creatively. For instance, we could create a method to find all the people who have the same first name as the current instance.
In this example, let’s create a function to return the initials for the current user. Let’s add a custom helper method called getInitials
to the schema:
This method will be accessible via a model instance:
Similar to instance methods, we can create static methods on the schema. Let’s create a method to retrieve all users in the database:
Calling getUsers
on the Model class will return all the users in the database:
Adding instance and static methods is a nice approach to implement an interface to database interactions on collections and records.
Middleware are functions that run at specific stages of a pipeline. Mongoose supports middleware for the following operations:
For instance, models have pre
and post
functions that take two parameters:
Let’s try an example by adding two fields called createdAt
and updatedAt
to our schema:
When model.save()
is called, there is a pre(‘save’, …)
and post(‘save’, …)
event that is triggered. For the second parameter, you can pass a function that is called when the event is triggered. These functions take a parameter to the next function in the middleware chain.
Let’s add a pre-save hook and set values for createdAt
and updatedAt
:
Let’s create and save our model:
You should see values for createdAt
and updatedAt
when the record that is created is printed:
Suppose that we want to track when a record was created and last updated on every collection in our database. Instead of repeating the above process, we can create a plugin and apply it to every schema.
Let’s create a file ./src/model/plugins/timestamp.js
and replicate the above functionality as a reusable module:
To use this plugin, we simply pass it to the schemas that should be given this functionality:
Mongoose has a very rich API that handles many complex operations supported by MongoDB. Consider a query where we can incrementally build query components.
In this example, we are going to:
We have barely scratched the surface exploring some of the capabilities of Mongoose. It is a rich library full of useful and and powerful features that make it a joy to work with data models in the application layer.
While you can interact with Mongo directly using Mongo Driver, Mongoose will simplify that interaction by allowing you to model relationships between data and validate them easily.
Fun Fact:Mongoose is created by Valeri Karpovwho is an incredibly talented engineer! He coined the term The MEAN Stack.
You can create clustered indexes on tables by using SQL Server Management Studio or Transact-SQL. With few exceptions, every table should have a clustered index. Besides improving query performance, a clustered index can be rebuilt or reorganized on demand to control table fragmentation. A clustered index can also be created on a view. (Clustered indexes are defined in the topic Clustered and Nonclustered Indexes Described.)
In This Topic
Before you begin:
To create a clustered index on a table, using:
Clustered indexes are implemented in the following ways:
PRIMARY KEY and UNIQUE constraints
When you create a PRIMARY KEY constraint, a unique clustered index on the column or columns is automatically created if a clustered index on the table does not already exist and you do not specify a unique nonclustered index. The primary key column cannot allow NULL values.
When you create a UNIQUE constraint, a unique nonclustered index is created to enforce a UNIQUE constraint by default. You can specify a unique clustered index if a clustered index on the table does not already exist.
An index created as part of the constraint is automatically given the same name as the constraint name. For more information, see Primary and Foreign Key Constraints and Unique Constraints and Check Constraints.
Index independent of a constraint
You can create a clustered index on a column other than primary key column if a nonclustered primary key constraint was specified.
When a clustered index structure is created, disk space for both the old (source) and new (target) structures is required in their respective files and filegroups. The old structure is not deallocated until the complete transaction commits. Additional temporary disk space for sorting may also be required. For more information, see Disk Space Requirements for Index DDL Operations.
If a clustered index is created on a heap with several existing nonclustered indexes, all the nonclustered indexes must be rebuilt so that they contain the clustering key value instead of the row identifier (RID). Similarly, if a clustered index is dropped on a table that has several nonclustered indexes, the nonclustered indexes are all rebuilt as part of the DROP operation. This may take significant time on large tables.
The preferred way to build indexes on large tables is to start with the clustered index and then build any nonclustered indexes. Consider setting the ONLINE option to ON when you create indexes on existing tables. When set to ON, long-term table locks are not held. This enables queries or updates to the underlying table to continue. For more information, see Perform Index Operations Online.
The index key of a clustered index cannot contain varchar columns that have existing data in the ROW_OVERFLOW_DATA allocation unit. If a clustered index is created on a varchar column and the existing data is in the IN_ROW_DATA allocation unit, subsequent insert or update actions on the column that would push the data off-row will fail. To obtain information about tables that might contain row-overflow data, use the sys.dm_db_index_physical_stats (Transact-SQL) dynamic management function.
Requires ALTER permission on the table or view. User must be a member of the sysadmin fixed server role or the db_ddladmin and db_owner fixed database roles.
In Object Explorer, expand the table on which you want to create a clustered index.
Right-click the Indexes folder, point to New Index, and select Clustered Index...
In the New Index dialog box, on the General page, enter the name of the new index in the Index name box.
Under Index key columns, click Add...
In the Select Columns fromtable_name dialog box, select the check box of the table column to be added to the clustered index.
Office mac 2011 product key generator free. Click OK.
In the New Index dialog box, click OK.
In Object Explorer, expand the database on which you want to create a table with a clustered index.
Right-click the Tables folder and click New Table...
Create a new table as you normally would. For more information, see Create Tables (Database Engine).
Right-click the new table created above and click Design.
On the Table Designer menu, click Indexes/Keys.
In the Indexes/Keys dialog box, click Add.
Select the new index in the Selected Primary/Unique Key or Index text box.
In the grid, select Create as Clustered, and choose Yes from the drop-down list to the right of the property.
Click Close.
On the File menu, click Savetable_name.
In Object Explorer, connect to an instance of Database Engine.
On the Standard bar, click New Query.
Copy and paste the following example into the query window and click Execute.
For more information, see CREATE INDEX (Transact-SQL).
Create Primary Keys
Create Unique Constraints