mongoose
Column 1 Applying a Plugin to a Schema Plugins in Mongoose are reusable sets of schema and model options that can be applied globally or on specific schemas. They help encapsulate common functionality, such as timestamps, into re-usable components.// Define the plugin
const timestampPlugin = require('./plugins/timestamp');
// Apply the plugin to a schema
const userSchema = new mongoose.Schema({
name: String,
});
userSchema.plugin(timestampPlugin); Plugin Hooks and Middleware Mongoose allows you to define middleware functions that execute before or after certain operations. These are useful for tasks like validation, encryption, logging, etc. You can also use plugin hooks to add reusable functionality across multiple schemas.// Example of using pre-save middleware
userSchema.pre('save', function(next) {
// perform some actions before saving the user
next();
}); Using Third-Party Plugins with Mongoose Mongoose allows the use of third-party plugins to extend its functionality. These can be used for adding custom validation, virtuals, middleware functions, and more. To use a plugin in your schema definition, simply call the `schema.plugin()` method.// Example using a hypothetical timestamp plugin
const mongoose = require('mongoose');
const timestampPlugin = require('./timestamp-plugin');
// Define schema
const userSchema = new mongoose.Schema({ name: String });
userSchema.plugin(timestampPlugin);
Use Only Necessary Plugins Only use plugins that are necessary for your application. Using unnecessary plugins can bloat the codebase and lead to performance issues.// Example of using a Mongoose plugin
const mongoose = require('mongoose');
const timestampPlugin = require('./plugins/timestamp');
mongoose.plugin(timestampPlugin); Built-in Mongoose Plugins (e.g., timestamps) Mongoose provides built-in plugins that can be used to add pre-defined functionality. One such plugin is 'timestamps' which automatically adds createdAt and updatedAt fields to the schema. This simplifies tracking when documents are created or modified.// Example of using timestamps plugin
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: String,
}, { timestamps: true }); Differences Between Global and Instance Level plugins Global plugins are defined on the schema level and apply to all instances of a model, while instance-level plugins are applied directly to specific document instances.// Define global plugin
const timestampPlugin = require('./plugins/timestamp');
schema.plugin(timestampPlugin);
// Apply instance-level plugin
const doc = new MyModel();
doc.myInstanceLevelFunction() Creating a Mongoose Plugin Mongoose plugins are reusable sets of schema and model methods that can be added to any schema. They provide an easy way to add common functionality across multiple schemas.// Define the plugin function
const myPlugin = (schema, options) => {
// Add custom methods or hooks to the schema here
};
// Apply the plugin to a specific schema
mySchema.plugin(myPlugin); Introduction to Mongoose Plugins Mongoose plugins are reusable sets of schema and model functionality that can be added to a Mongoose schema. They allow you to add pre-defined features or methods across multiple schemas, making your code more modular and maintainable.// Define the plugin
const myPlugin = (schema) => {
// Add custom functionality here
};
// Use the plugin in a schema
mySchema.plugin(myPlugin); Middleware Functions Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle. They can perform tasks, modify requests or responses, end a request-response cycle, call another middleware function in the stack, etc.// Example of a simple middleware function
function myMiddleware(req, res,next) {
// Perform some task here
next();
} Types of Middleware in Mongoose Mongoose supports four types of middleware: document middleware, model middleware, aggregate middleware, and query helpers. Document middleware is executed before or after a specific operation on an instance (i.e., a document). Model middlewares are for operations at the schema level such as 'init', 'validate', etc. Aggregate middlware allows defining functions that execute when calling `aggregate()` function. Query helper methods enable adding custom static methods to models.// Example of using pre-save hook
schema.pre('save', async function(next) {
// Perform some actions before saving the document
next();
}); Order of Execution for Multiple Middlewares When using multiple middlewares in Mongoose, the order of execution is crucial. Middlewares are executed serially based on their registration sequence. Use next() to move to the next middleware or end the process.// Example
schema.pre('save', function(next) {
// Do something before saving
next();
}); Defining and Using Custom Middleware Functions Custom middleware functions can be defined using the `use` method on a schema. These functions are executed before or after certain operations such as validation, saving, updating, etc. They provide a way to perform custom logic or modify data during these operations.// Define custom pre-save middleware
schema.pre('save', function(next) {
// Perform some logic before saving
next();
}); Error Handling in Middlewares When creating custom middleware for error handling, use the 'next' function with an argument to pass errors. Use a conditional statement to check if there is an error and then call next(error) within the middleware. Always define your error-handling middlewares last after all other app.use() and routes.// Custom Error Handling Middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
}); Global Middleware vs. Document Middleware Global middleware functions are defined on the schema and apply to all documents in a collection, while document middleware functions are specific to individual documents.// Global Middleware
schema.pre('save', function(next) {
// Do something before saving
next();
});
// Document Middleware
schema.methods.methodName = function() {
// Define method for individual document
}; Use Middleware for Cross-cutting Concerns Middleware can be used to handle common tasks such as logging, authentication, error handling, and input validation. This helps in keeping the codebase clean by separating concerns and promoting reusability.// Example of using middleware
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('Logging request...');
next(); // Pass control to the next middleware function
}); Using Async/Await with Mongoose middleware functions When using async/await with Mongoose middleware, you need to remember that the next() function is not automatically called. You must explicitly call it after your asynchronous operation completes. Also, make sure to handle any errors by wrapping your code in a try-catch block.const UserSchema = new mongoose.Schema({
name: String,
});
UserSchema.pre('save', async function(next) {
try{
// Perform some asynchronous operation here
await somethingAsync(); // Remember to use 'await'
n next(); // Call 'next' when done
} catch(err){
c onsole.error(err); // Handle error appropriately
}
}); find() The find() method is used to retrieve documents from a MongoDB collection. It takes an optional query object as its parameter, which allows you to filter the results based on specific criteria. If no query object is provided, it will return all documents in the collection. // Find all documents
Model.find()
// Find with a query
Model.find({ key: value }) sort() The sort() method is used to specify the sorting order for query results in Mongoose. It takes an object as a parameter, where each key represents a field and its value determines the sorting order (1 for ascending, -1 for descending). Multiple fields can be specified to create compound sorts. .find().sort({ fieldName: sortOrder }) Column 2 Select The select method is used to specify the fields that should be returned in the query result. It allows you to control which fields are retrieved from a document, helping reduce network overhead and improve performance..select('field1 field2') Limit The limit() function is used to restrict the number of documents returned by a query. It takes an integer as its argument, representing the maximum number of documents to be returned..find().limit(5) // Returns only up to 5 documents Where Query The 'where' query in Mongoose allows you to specify conditions for the documents returned by a find() or findOne() method. It is useful for filtering data based on specific criteria..find().where('field').equals(value) Populate The populate method in Mongoose is used to fill references from other collections. It allows you to retrieve documents referenced by a specific field and replace the specified path with actual data..populate('fieldName') Sort Mongoose provides the ability to sort query results based on one or more fields. Use the `sort()` method in combination with `-1` for descending order and `1` for ascending order. The sorting is applied after all other operations like filtering, projecting, etc..find().sort({ fieldName: -1 }) // Sorts by 'fieldName' in descending order $and and $or operators for complex conditions The $and operator performs a logical AND operation on an array of two or more expressions, returning true only if all the expressions are true. The $or operator performs a logical OR operation on an array of two or more expressions, returning true if any expression is true.// Using Mongoose to find documents matching multiple conditions
Model.find({
$and: [
{ condition1 },
{ condition2 }
]
}); $elemMatch operator for array filtering The $elemMatch operator is used to query and filter documents that contain arrays. It ensures that at least one element in the array matches all the specified criteria. This can be useful when querying embedded documents within an array field.// Find documents where 'scores' contains a subdocument with both 'type' equal to 'quiz' and 'score' greater than or equal to 90
Model.find({ scores: { $elemMatch: { type: \"quiz\", score: { $gte : 90 } } }}) Count documents with count() method The count() method is used to retrieve the number of documents that match a query. It takes a query as an optional parameter and returns the count of matching documents. If no query is provided, it returns the total document count in the collection.// Count all documents in 'users' collection
const userCount = await User.count();
// Count specific documents based on condition
const activeUserCount = await User.count({ isActive: true }); Number In Mongoose, the Number schema type is used to define a path for numeric values. It can be configured with options such as min, max, and default values. When defining a number field in a Mongoose schema, you can specify additional validation rules using 'min' and 'max'. For example: { age: { type: Number, min: 18 } }.// Define a Mongoose Schema with a number field
const userSchema = new mongoose.Schema({
age: {
type : Number,
required : true,
min : [0,'Age must be greater than or equal to zero']
}
}); Buffer Buffers are Node.js objects used to represent binary data. They act as a temporary storage for raw data during the transmission of files or when working with streams. Buffers can be converted to strings and vice versa using encoding methods.// Creating a buffer from string
const buf = Buffer.from('Hello, World!', 'utf-8');
// Converting buffer to string
console.log(buf.toString('utf-8')); String in Mongoose In Mongoose, the String schema type is used to define a path for storing string values. It can be configured with additional options such as required, default value, and validation rules using built-in or custom validators.// Defining a 'name' field of type String
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true
}
}); Date in Mongoose Mongoose has a built-in Date schema type for handling dates. Dates can be set as default values, validated against min and max limits, or used with custom validation logic.// Example of using the Date schema type
const eventSchema = new mongoose.Schema({
date: {
type: Date,
required: true,
default: Date.now()
}
}); Boolean In Mongoose, the Boolean data type represents a true/false value. It is commonly used to store binary states such as 'active' or 'inactive'. When defining a schema in Mongoose, you can use the type key with the value set to Boolean.// Define a schema with a field of type boolean
const userSchema = new mongoose.Schema({
isActive: {type: Boolean}
}); ObjectId The ObjectId is a unique identifier for documents in MongoDB. It consists of a timestamp, machine id, process id, and counter. The first four bytes represent the timestamp in seconds since the Unix epoch.// Creating an ObjectId
const { ObjectID } = require('mongodb');
const objectId = new ObjectID(); Schema Types Mongoose provides several built-in SchemaTypes to define the data structure for each field in a document. These include String, Number, Date, Buffer (for storing binary data), Boolean, Mixed (allows any type of data), ObjectId and Array.// Example defining an array field
const userSchema = new mongoose.Schema({
name: String,
age: Number,
hobbies: [String]
}); Mixed The 'Mixed' schema type in Mongoose allows flexibility by storing data that doesn't have a fixed structure. It can hold any arbitrary data types, making it suitable for scenarios where the shape of the documents may vary.// Example of using Mixed type
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Define a schema with mixed type field
const mySchema = new Schema({
dynamicData: {}
}); Creating a Mongoose model Mongoose provides a straightforward way to create models for your MongoDB collections. Models are constructors compiled from Schema definitions and represent documents in the database.// Define schema
const { Schema } = mongoose;
const userSchema = new Schema({
name: String,
age: Number
});
// Create model using schema
const User = mongoose.model('User', userSchema); Defining schema for the model In Mongoose, a schema defines the structure of documents within a collection. It includes fields and their types along with options such as default values, validation rules, etc. Schemas are used to create models which can be instantiated into document instances.// Define a simple user schema
const userSchema = new mongoose.Schema({
name: String,
email: { type: String, required: true }
}); Specifying field types and validations Mongoose allows you to specify the data type of each field in a schema, along with various validation options. This helps ensure that only valid data is stored in the database. Commonly used validators include required, min/max length, enum (for specifying allowed values), match (to validate against a regular expression), and custom validator functions.// Example of defining a schema with field types and validations
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
age: { type: Number, min: [18,'Must be at least 18 years old'] }
}); Using plugins in models Mongoose allows the use of plugins to add reusable functionality to your schema. Plugins can be used for adding fields, methods, statics or virtuals to a model.// Define and apply a plugin
const timestampPlugin = require('./plugins/timestamp');
schema.plugin(timestampPlugin);
Setting up middleware/hooks for pre/post actions Mongoose allows you to define middleware functions that execute before or after certain operations such as validation, saving, and removing. Use the `pre` method to register a function to run before the specified operation and use the `post` method for running it after. These hooks are useful for tasks like data manipulation, logging, authentication checks.// Pre save hook
schema.pre('save', function(next) {
// Your code here...
next();
}); Adding methods to the model You can add instance and static methods to your Mongoose models. Instance methods operate on an individual document, while static methods work at the Model level. Use `schema.methods.methodName` for adding instance method and `schema.statics.methodName` for adding a static method.// Adding an instance method
const schema = new mongoose.Schema({
name: String,
});
schema.methods.speak = function () {
const greeting = this.name ? 'My name is ' + this.name : \"I don't have a name\";
console.log(greeting);
}; Including virtual properties in the schema Virtuals are document properties that you can get and set but do not persist to MongoDB. They're great for formatting or combining fields, as well as creating new data on the fly.// Define a virtual property
const personSchema = new Schema({
firstName: String,
lastName: String
});
personSchema.virtual('fullName').get(function() {
return this.firstName + ' ' + this.lastName;
}); Applying options such as timestamps Mongoose provides the option to include a createdAt and updatedAt field in your schema, which automatically updates these fields when documents are created or modified. This is achieved by setting the 'timestamps' option to true when defining the schema.// Example of applying timestamps
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: String,
}, { timestamps: true }); Column 3 Defining a Schema In Mongoose, a schema defines the structure of documents within a collection. It specifies the fields and their types along with any additional options such as default values or validation rules. Schemas are used to create models which can then be used to perform CRUD operations on MongoDB collections.// Define a simple schema
const { Schema } = require('mongoose');
const userSchema = new Schema({
name: String,
age: Number
}); Data Types and Validation Mongoose provides various data types such as String, Number, Date, Boolean, etc., for defining schema paths. It also offers built-in validators like required, min/max length, enum values to enforce data validation rules. Custom validators can be defined using the validate property in a schema path definition.// Defining a Mongoose Schema with Data Types and Validators
const userSchema = new mongoose.Schema({
name: { type: String , required: true },
age: { type : Number , min : [18,'Must be at least 18 years old'] }
}); Required Fields In Mongoose, you can specify that certain fields are required using the 'required' property in a schema definition. This ensures that documents must contain these fields before they can be saved to the database. If a document is missing a required field, Mongoose will throw a validation error.// Example of defining a required field in Mongoose
const userSchema = new mongoose.Schema({
name: { type: String, required: true }
}); Error Messages for Validation Failures Mongoose provides built-in error messages for validation failures. These include 'required', 'minlength', 'maxlength', and more, which can be customized as needed.// Example of customizing error message
const schema = new Schema({
name: { type: String, required: true, minlength: [3, 'Name must be at least 3 characters'] }
}) Schema Options (e.g., strict) The 'strict' option in Mongoose schema determines whether fields other than those defined in the schema are ignored or throw an error. Setting 'strict: true' means that any data not specified in the schema will be discarded, while setting it to false allows for additional data.// Example of using strict option
const sampleSchema = new Schema({
name: String,
age: Number
}, { strict: true }); Custom Validators Mongoose allows you to define custom validators for your schema. You can create reusable validation functions and apply them to specific fields in your schema. Custom validators are useful for enforcing complex business rules or data requirements.// Define a custom validator function
const myValidator = (value) => {
if (/* some condition */) {
return true;
}
return false;
};
// Apply the custom validator to a field in the schema
const mySchema = new Schema({
customField: { type: String, validate: [myValidator, 'Invalid value'] },
}); Asynchronous Custom Validators Mongoose allows you to define custom validators that can perform asynchronous validation. This is useful for scenarios where the validation requires querying a database or making an API call. To create an asynchronous custom validator, use a function with the `validate` property set to true and accept a callback as its last argument.// Example of defining an asynchronous custom validator
const userSchema = new Schema({
email: {
type: String,
validate: {
isAsync: true,
validator: function(value, isValid) {
setTimeout(() => { // Simulating async operation
const result = value && value.length > 0; // Perform actual async check here isValid(result); }, 5); }
type: tString}); Inbuilt Validators Mongoose provides inbuilt validators for data validation before saving it to the database. These include built-in validator types such as required, min, max, enum, match (regex), and more.// Example of using inbuilt validators
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
age: { type: Number, min: [18,'Must be at least 18'], max:[100,'Cannot exceed 100'] }
}); Populate Method The populate method in Mongoose is used to automatically replace specified paths with documents from other collections. It allows for seamless integration of data across different models and simplifies querying by eliminating the need for manual population..populate('author', 'name -_id') Update Method The update method in Mongoose allows you to modify existing documents in MongoDB. It takes two parameters: the query to find the document(s) and an object containing the updates..update({ name: 'John' }, { age: 30 }) Creating New Documents Mongoose provides a simple way to create new documents using models. Use the 'save' method on a new instance of the model to persist it in MongoDB. You can also use the 'create' method directly on the model class, which combines creating and saving into one step.// Using save method
const newUser = new User({ name: 'John', age: 25 });
await newUser.save();
// Using create method
const createdUser = await User.create({ name: 'Jane', age: 30 }); Population Populating referenced document fields during a read operation allows you to retrieve documents from other collections and include them in the result. This is useful for avoiding multiple database queries when working with related data..populate('fieldName') Mongoose Delete Methods Mongoose provides several methods to delete documents from a collection, including findByIdAndDelete(), findOneAndRemove(), and deleteOne(). These methods allow you to remove specific documents based on conditions or criteria.// Example using findByIdAndDelete
const result = await YourModel.findByIdAndDelete(id); Middleware Hooks Mongoose provides pre-save and post-save hooks that allow you to run custom logic before or after CRUD operations. These hooks are useful for tasks such as data validation, encryption, logging, etc.// Pre-Save Hook
schema.pre('save', function(next) {
// Custom logic here
next();
});
// Post-Save Hook
schema.post('save', function(doc) {
// Custom logic here
}); Validation in Mongoose Mongoose provides a powerful schema-based validation feature to ensure data integrity. You can define validators for each field, including built-in and custom ones. Use the 'validate' property within your schema definition to specify validation rules.// Example of defining a validator for a field
const userSchema = new Schema({
age: {
type: Number,
validate: {
validator: function(v) { return v > 0; },
message: 'Age must be greater than zero'
}
} }); Bulk Operations Mongoose provides efficient methods for performing bulk insert, update, and delete operations. These operations can be performed using Mongoose's built-in functions like 'insertMany', 'updateMany', and 'deleteMany'. Performing these bulk operations helps in optimizing database interactions by reducing the number of individual queries.// Example of Bulk Insert
const data = [{ name: 'John' }, { name: 'Jane' }];
Model.insertMany(data); Defining a schema with mongoose.Schema() method The `mongoose.Schema()` method is used to define the structure of documents within a collection. It takes an object that defines the shape of your documents, including properties and their types. You can also specify additional options such as default values, validation rules, and custom getters/setters.// Define a simple schema
const userSchema = new mongoose.Schema({
name: String,
age: Number
}); Installing Mongoose To install Mongoose, use npm (Node Package Manager) by running the command 'npm install mongoose' in your terminal. This will download and add the Mongoose package to your Node.js project. Make sure you have MongoDB installed and running before using Mongoose.$ npm install mongoose Creating a model from the schema using mongoose.model() method The mongoose.model() method takes two parameters: the singular name of the collection and the schema. It returns a Model, which can be used to perform CRUD operations on that collection. The first parameter should be in singular form (e.g., 'User' for a users collection). If you don't specify an existing MongoDB connection when calling this function, Mongoose will create one by default.// Define userSchema
const userSchema = new Schema({
name: String,
age: Number
});
// Create User model based on userSchema
const User = mongoose.model('User', userSchema); Connecting to MongoDB using Mongoose Mongoose provides a connect method to establish the connection with MongoDB. The connect method takes a URI as its first argument, followed by optional options and callback function. It also supports multiple connections by creating separate instances of mongoose.// Establishing connection
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/myDatabase', { useNewUrlParser: true }); Handling Connection Errors When connecting to MongoDB using Mongoose, you can handle connection errors by listening for the 'error' event on the mongoose.connection object. You can also use try-catch blocks when establishing connections to catch any potential errors. Additionally, setting up retry logic or implementing error handling middleware in your Node.js application can help manage and recover from connection errors.// Handling connection error example
mongoose.connect('mongodb://localhost/myapp', { useNewUrlParser: true });
mongoose.connection.on('error', (err) => {
console.error(`Mongoose default connection error: ${err}`);
}); Importing Mongoose into Node.js app To use Mongoose in a Node.js application, first install it using npm: 'npm install mongoose'. Then, require the mongoose module in your code to create a connection and define schemas for MongoDB. Use the connect() method to establish a connection with your MongoDB database.// Importing Mongoose
const mongoose = require('mongoose');
// Connecting to MongoDB using Mongoose
mongoose.connect('mongodb://localhost/my_database', {useNewUrlParser: true}); Establishing Default Connection Mongoose allows you to establish a default connection for models and schemas using the `mongoose.connect()` method. This is typically done when setting up your application, such as in the main entry file. The default connection can be reused across multiple modules by importing mongoose.// Example of establishing a default connection
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/myDatabase', { useNewUrlParser: true, useUnifiedTopology: true }); Closing the database connection It's important to close the Mongoose connection when your Node.js application is shutting down or no longer needs to interact with MongoDB. This prevents memory leaks and ensures that all operations are completed before exiting.// Close the Mongoose connection
mongoose.connection.close(() => {
console.log('Connection closed');
}); |