How To Repair Mongoose Forks
by Nick Karnik
Introduction to Mongoose for MongoDB
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 interpret betwixt objects in code and the representation of those objects in MongoDB.
MongoDB is a schema-less NoSQL certificate database. It means y'all 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 upward application development and reduces the complexity of deployments.
Beneath is an example of how data is stored in Mongo vs. SQL Database:
Terminologies
Collections
'Collections' in Mongo are equivalent to tables in relational databases. They tin can concur multiple JSON documents.
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
'Fields' or attributes are similar to columns in a SQL table.
Schema
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
'Models' are higher-guild constructors that accept a schema and create an example of a document equivalent to records in a relational database.
Getting Started
Mongo Installation
Before nosotros get started, let's setup Mongo. You can choose from i of the following options (we are using option #i for this commodity):
- Download the appropriate MongoDB version for your Operating Organisation from the MongoDB Website and follow their installation instructions
- Create a costless sandbox database subscription on mLab
- Install Mongo using Docker if you adopt to utilise docker
Allow'south navigate through some of the nuts of Mongoose by implementing a model that represents data for a simplified accost book.
I am using Visual Studio Code, Node viii.ix, and NPM 5.6. Burn down upwardly your favorite IDE, create a blank project, and permit'due south get started! Nosotros volition be using the limited ES6 syntax in Node, so we won't be configuring Babel.
NPM Install
Let's go to the projection folder and initialize our project
npm init -y Let'due south install Mongoose and a validation library with the post-obit command:
npm install mongoose validator The above install command will install the latest version of the libraries. The Mongoose syntax in this commodity is specific to Mongoose v5 and beyond.
Database Connection
Create a file ./src/database.js under the project root.
Next, we will add together a simple class with a method that connects to the database.
Your connection string will vary based on your installation.
let mongoose = require('mongoose'); const server = '127.0.0.1:27017'; // REPLACE WITH YOUR DB SERVER const database = 'fcc-Mail'; // REPLACE WITH YOUR DB Name class Database { constructor() { this._connect() } _connect() { mongoose.connect(`mongodb://${server}/${database}`) .then(() => { panel.log('Database connection successful') }) .catch(err => { console.error('Database connectedness error') }) } } module.exports = new Database() The require('mongoose') call to a higher place returns a Singleton object. Information technology 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 case that was created and returned to you the first time because of how module import/export works in ES6.
Similarly, we take turned our Database class into a singleton past returning an instance of the class in the module.exports argument because we only need a single connectedness to the database.
ES6 makes information technology very piece of cake for usa to create a singleton (single instance) design because of how the module loader works by caching the response of a previously imported file.
Mongoose Schema vs. Model
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 3 parts:
one. Referencing Mongoose
permit mongoose = require('mongoose') This reference will be the same as the ane that was returned when we continued to the database, which means the schema and model definitions will not need to explicitly connect to the database.
ii. Defining the Schema
A schema defines certificate properties through an object where the key name corresponds to the belongings name in the collection.
let emailSchema = new mongoose.Schema({ electronic mail: String }) Hither 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 volition fail if the data type of the value is not a string blazon.
The post-obit Schema Types are permitted:
- Array
- Boolean
- Buffer
- Date
- Mixed (A generic / flexible data type)
- Number
- ObjectId
- String
Mixed and ObjectId are defined nether require('mongoose').Schema.Types.
three. Exporting a Model
We need to call the model constructor on the Mongoose instance and pass it the proper name of the collection and a reference to the schema definition.
module.exports = mongoose.model('Email', emailSchema) Let'south combine the above code into ./src/models/email.js to define the contents of a basic email model:
allow mongoose = require('mongoose') let emailSchema = new mongoose.Schema({ email: String }) module.exports = mongoose.model('Electronic mail', emailSchema) A schema definition should be simple, but its complexity is normally based on application requirements. Schemas tin be reused and they can incorporate several child-schemas as well. In the example above, the value of the electronic mail property is a simple value type. However, it can also be an object type with additional properties on it.
We tin create an instance of the model we defined above and populate it using the following syntax:
let EmailModel = require('./email') allow msg = new EmailModel({ email: 'ada.lovelace@gmail.com' }) Let'southward enhance the Email schema to brand the email property a unique, required field and catechumen the value to lowercase before saving information technology. We can also add a validation part that volition ensure that the value is a valid email address. We will reference and use the validator library installed earlier.
let mongoose = require('mongoose') let validator = require('validator') let emailSchema = new mongoose.Schema({ email: { type: String, required: truthful, unique: true, lowercase: true, validate: (value) => { return validator.isEmail(value) } } }) module.exports = mongoose.model('Email', emailSchema) Basic Operations
Mongoose has a flexible API and provides many means to reach a task. We volition not focus on the variations because that is out of scope for this article, but remember that well-nigh of the operations can be done in more than 1 manner either syntactically or via the application architecture.
Create Record
Let's create an instance of the email model and relieve it to the database:
let EmailModel = require('./email') allow msg = new EmailModel({ email: 'ADA.LOVELACE@GMAIL.COM' }) msg.relieve() .then(dr. => { panel.log(doc) }) .catch(err => { console.fault(err) }) The result is a document that is returned upon a successful save:
{ _id: 5a78fe3e2f44ba8f85a2409a, e-mail: 'ada.lovelace@gmail.com', __v: 0 } The following fields are returned (internal fields are prefixed with an underscore):
- The
_idfield is machine-generated by Mongo and is a primary fundamental of the collection. Its value is a unique identifier for the document. - The value of the
e-mailfield is returned. Notice that it is lower-cased considering nosotros specified thelowercase:trueattribute in the schema. -
__vis the versionKey property fix on each document when showtime created by Mongoose. Its value contains the internal revision of the document.
If yous try to repeat the save performance to a higher place, yous will get an error because nosotros have specified that the email field should be unique.
Fetch Record
Let'south try to retrieve the record we saved to the database before. The model class exposes several static and example methods to perform operations on the database. We will now try to find the record that we created previously using the notice method and pass the e-mail equally the search term.
EmailModel .discover({ email: 'ada.lovelace@gmail.com' // search query }) .then(doc => { console.log(doc) }) .catch(err => { console.fault(err) }) The document returned will exist similar to what was displayed when we created the record:
{ _id: 5a78fe3e2f44ba8f85a2409a, electronic mail: 'ada.lovelace@gmail.com', __v: 0 } Update Record
Let'south modify the record above by changing the e-mail address and adding some other field to information technology, all in a single operation. For performance reasons, Mongoose won't return the updated document and so nosotros need to laissez passer an boosted parameter to ask for it:
EmailModel .findOneAndUpdate( { e-mail: 'ada.lovelace@gmail.com' // search query }, { email: 'theoutlander@live.com' // field:values to update }, { new: truthful, // return updated dr. runValidators: true // validate earlier update }) .then(doc => { console.log(dr.) }) .catch(err => { console.error(err) }) The document returned volition comprise the updated electronic mail:
{ _id: 5a78fe3e2f44ba8f85a2409a, email: 'theoutlander@live.com', __v: 0 } Delete Tape
We will use the findOneAndRemove phone call to delete a record. It returns the original document that was removed:
EmailModel .findOneAndRemove({ email: 'theoutlander@alive.com' }) .and then(response => { console.log(response) }) .catch(err => { console.mistake(err) }) Helpers
Nosotros take looked at some of the basic functionality to a higher place known as Crud (Create, Read, Update, Delete) operations, but Mongoose too provides the ability to configure several types of helper methods and properties. These tin exist used to further simplify working with data.
Allow's create a user schema in ./src/models/user.js with the fieldsfirstName and lastName:
let mongoose = crave('mongoose') let userSchema = new mongoose.Schema({ firstName: Cord, lastName: Cord }) module.exports = mongoose.model('User', userSchema) Virtual Belongings
A virtual belongings is not persisted to the database. Nosotros tin can add it to our schema as a helper to get and prepare values.
Let'due south create a virtual property chosen fullName which tin exist used to set values on firstName and lastName and think them as a combined value when read:
userSchema.virtual('fullName').get(role() { return this.firstName + ' ' + this.lastName }) userSchema.virtual('fullName').fix(office(name) { permit str = name.carve up(' ') this.firstName = str[0] this.lastName = str[1] }) Callbacks for get and gear up must use the office keyword as we need to access the model via the this keyword. Using fat arrow functions will alter what this refers to.
Now, we tin can gear up firstName and lastName by assigning a value to fullName:
let model = new UserModel() model.fullName = 'Thomas Anderson' console.log(model.toJSON()) // Output model fields as JSON console.log() console.log(model.fullName) // Output the full name The code above will output the following:
{ _id: 5a7a4248550ebb9fafd898cf, firstName: 'Thomas', lastName: 'Anderson' } Thomas Anderson Instance Methods
Nosotros 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 case, nosotros could create a method to find all the people who have the same first proper name equally the electric current instance.
In this instance, let's create a function to return the initials for the current user. Allow'south add together a custom helper method called getInitials to the schema:
userSchema.methods.getInitials = role() { return this.firstName[0] + this.lastName[0] } This method will be attainable via a model case:
let model = new UserModel({ firstName: 'Thomas', lastName: 'Anderson' }) let initials = model.getInitials() panel.log(initials) // This will output: TA Static Methods
Like to example methods, we can create static methods on the schema. Let's create a method to retrieve all users in the database:
userSchema.statics.getUsers = part() { return new Promise((resolve, reject) => { this.find((err, docs) => { if(err) { console.error(err) render turn down(err) } resolve(docs) }) }) } Calling getUsers on the Model course will return all the users in the database:
UserModel.getUsers() .then(docs => { console.log(docs) }) .catch(err => { panel.error(err) }) Adding instance and static methods is a nice approach to implement an interface to database interactions on collections and records.
Middleware
Middleware are functions that run at specific stages of a pipeline. Mongoose supports middleware for the post-obit operations:
- Aggregate
- Certificate
- Model
- Query
For instance, models take pre and post functions that take two parameters:
- Blazon of event ('init', 'validate', 'save', 'remove')
- A callback that is executed with this referencing the model example
Let'southward effort an example past calculation two fields chosen createdAt and updatedAt to our schema:
let mongoose = crave('mongoose') let userSchema = new mongoose.Schema({ firstName: String, lastName: Cord, createdAt: Date, updatedAt: Date }) module.exports = mongoose.model('User', userSchema) 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 issue is triggered. These functions take a parameter to the adjacent role in the middleware chain.
Allow'southward add a pre-relieve claw and fix values for createdAt and updatedAt:
userSchema.pre('relieve', function (adjacent) { allow now = Engagement.now() this.updatedAt = at present // Set a value for createdAt only if information technology is zilch if (!this.createdAt) { this.createdAt = now } // Call the side by side office in the pre-save concatenation side by side() }) Let'southward create and salvage our model:
let UserModel = require('./user') let model = new UserModel({ fullName: 'Thomas Anderson' } msg.save() .then(doc => { console.log(doctor) }) .catch(err => { console.fault(err) }) You should meet values for createdAt and updatedAt when the tape that is created is printed:
{ _id: 5a7bbbeebc3b49cb919da675, firstName: 'Thomas', lastName: 'Anderson', updatedAt: 2022-02-08T02:54:38.888Z, createdAt: 2022-02-08T02:54:38.888Z, __v: 0 } Plugins
Suppose that we desire to runway when a tape was created and terminal updated on every collection in our database. Instead of repeating the above process, nosotros tin 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 every bit a reusable module:
module.exports = part timestamp(schema) { // Add the ii fields to the schema schema.add together({ createdAt: Engagement, updatedAt: Appointment }) // Create a pre-salve hook schema.pre('salve', function (next) { allow now = Date.at present() this.updatedAt = now // Gear up a value for createdAt only if it is zilch if (!this.createdAt) { this.createdAt = now } // Call the next office in the pre-save chain next() }) } To employ this plugin, nosotros but pass it to the schemas that should be given this functionality:
let timestampPlugin = require('./plugins/timestamp') emailSchema.plugin(timestampPlugin) userSchema.plugin(timestampPlugin) Query Building
Mongoose has a very rich API that handles many complex operations supported by MongoDB. Consider a query where nosotros can incrementally build query components.
In this instance, we are going to:
- Discover all users
- Skip the first 100 records
- Limit the results to ten records
- Sort the results by the firstName field
- Select the firstName
- Execute that query
UserModel.find() // detect all users .skip(100) // skip the starting time 100 items .limit(x) // limit to 10 items .sort({firstName: ane} // sort ascending by firstName .select({firstName: true} // select firstName only .exec() // execute the query .then(docs => { console.log(docs) }) .grab(err => { console.error(err) }) Endmost
We have barely scratched the surface exploring some of the capabilities of Mongoose. Information technology is a rich library full of useful and and powerful features that make it a joy to work with data models in the awarding layer.
While you can interact with Mongo direct using Mongo Commuter, Mongoose will simplify that interaction by allowing you to model relationships betwixt information and validate them easily.
Fun Fact: Mongoose is created past Valeri Karpov who is an incredibly talented engineer! He coined the term The Hateful Stack.
If this article was helpful, ??? and Follow me on Twitter.
Learn to code for gratis. freeCodeCamp's open source curriculum has helped more than forty,000 people get jobs as developers. Get started
Source: https://www.freecodecamp.org/news/introduction-to-mongoose-for-mongodb-d2a7aa593c57/
Posted by: comptonsterly.blogspot.com

0 Response to "How To Repair Mongoose Forks"
Post a Comment