MongoDB is a NoSQL database that stores data in a flexible, JSON-like format. Unlike relational databases that use rigid schemas, MongoDB allows you to store documents of varying structures within the same collection. While this flexibility is powerful, it can lead to data inconsistencies. Mongoose, an ODM (Object Data Modeling) library for Node.js, addresses this issue by introducing schemas and models. These tools bring structure and validation to your data while working with MongoDB.
This document provides an in-depth guide to defining schemas and models using Mongoose. We will cover concepts, syntax, best practices, and real-world examples to help you understand and apply them effectively.
A schema in Mongoose defines the structure and rules for a document in a MongoDB collection. It acts like a blueprint for how your data should look, including field names, data types, default values, required fields, and custom validations.
You can create a schema using the mongoose.Schema constructor. Here's a basic example:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: String,
age: Number,
email: String
});
Mongoose supports a variety of schema types that map directly to native JavaScript types:
const productSchema = new Schema({
name: String,
price: Number,
isAvailable: Boolean,
tags: [String],
metadata: {
weight: Number,
manufacturer: String
}
});
You can make a field required by setting required: true.
const userSchema = new Schema({
username: {
type: String,
required: true
}
});
const userSchema = new Schema({
email: {
type: String,
required: [true, 'User email is required']
}
});
const userSchema = new Schema({
username: {
type: String,
minlength: 4,
maxlength: 12,
match: /^[a-zA-Z0-9]+$/
}
});
const ratingSchema = new Schema({
score: {
type: Number,
min: 0,
max: 5
}
});
You can assign default values to fields that will be used if no value is provided.
const postSchema = new Schema({
title: String,
createdAt: {
type: Date,
default: Date.now
}
});
You can define nested schemas to structure documents more effectively.
const addressSchema = new Schema({
street: String,
city: String,
zipCode: String
});
const userSchema = new Schema({
name: String,
address: addressSchema
});
Arrays can be used to store multiple values of a certain type.
const blogSchema = new Schema({
title: String,
tags: [String],
comments: [{
body: String,
date: Date
}]
});
const userSchema = new Schema({
role: {
type: String,
enum: ['user', 'admin', 'moderator']
}
});
const productSchema = new Schema({
price: {
type: Number,
validate: {
validator: function(v) {
return v > 0;
},
message: props => `${props.value} is not a positive number!`
}
}
});
A model in Mongoose is a constructor compiled from a schema. An instance of a model is called a document. Models are responsible for creating and reading documents from the underlying MongoDB database.
const User = mongoose.model('User', userSchema);
const newUser = new User({
name: 'John Doe',
email: 'john@example.com',
age: 30
});
newUser.save()
.then(user => console.log(user))
.catch(err => console.error(err));
User.find({ age: { $gt: 18 } })
.then(users => console.log(users));
Schema methods allow you to define custom functions that can be called on document instances.
userSchema.methods.sayHello = function() {
return `Hello, my name is ${this.name}`;
};
const User = mongoose.model('User', userSchema);
const user = new User({ name: 'Mike' });
console.log(user.sayHello());
Statics are functions that are called on the model itself.
userSchema.statics.findByEmail = function(email) {
return this.findOne({ email });
};
const User = mongoose.model('User', userSchema);
User.findByEmail('test@example.com')
.then(user => console.log(user));
Virtuals are document properties that are not stored in MongoDB but computed on the fly.
userSchema.virtual('fullName')
.get(function() {
return `${this.firstName} ${this.lastName}`;
});
Indexes improve the performance of read operations.
userSchema.index({ email: 1 }, { unique: true });
Middleware are functions that run during different phases of a modelβs lifecycle.
userSchema.pre('save', function(next) {
console.log('About to save user...');
next();
});
userSchema.post('save', function(doc) {
console.log(`${doc.name} was saved.`);
});
Discriminators allow you to create multiple models from the same schema with slight variations.
const options = { discriminatorKey: 'kind' };
const eventSchema = new Schema({ time: Date }, options);
const Event = mongoose.model('Event', eventSchema);
const ClickedLink = Event.discriminator('ClickedLink',
new Schema({ url: String }, options));
References allow you to store ObjectId references to other documents and populate them as needed.
const commentSchema = new Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
text: String
});
Comment.find()
.populate('user')
.exec()
.then(comments => console.log(comments));
A function passed as an argument and executed later.
Runs multiple instances to utilize multi-core systems.
Reusable blocks of code, exported and imported using require() or import.
nextTick() executes before setImmediate() in the event loop.
Starts a server and listens on specified port.
Node Package Manager β installs, manages, and shares JavaScript packages.
A minimal and flexible web application framework for Node.js.
A stream handles reading or writing data continuously.
It processes asynchronous callbacks and non-blocking I/O operations efficiently.
Node.js is a JavaScript runtime built on Chrome's V8 engine for server-side scripting.
An object representing the eventual completion or failure of an asynchronous operation.
require is CommonJS; import is ES6 syntax (requires transpilation or newer versions).
Use module.exports or exports.functionName.
Variables stored outside the code for configuration, accessed using process.env.
MongoDB, often used with Mongoose for schema management.
Describes project details and manages dependencies and scripts.
Synchronous blocks execution; asynchronous runs in background without blocking.
Allows or restricts resources shared between different origins.
Use try-catch, error events, or middleware for error handling.
Provides file system-related operations like read, write, delete.
Using event-driven architecture and non-blocking I/O.
Functions in Express that execute during request-response cycle.
A set of routes or endpoints to interact with server logic or databases.
Yes, it's single-threaded but handles concurrency using the event loop and asynchronous callbacks.
Middleware to parse incoming request bodies, like JSON or form data.
Copyrights © 2024 letsupdateskills All rights reserved