阿里云主机折上折
  • 微信号
Current Site:Index > The core features of Mongoose

The core features of Mongoose

Author:Chuan Chen 阅读数:16964人阅读 分类: MongoDB

Mongoose is an excellent Node.js object modeling tool designed specifically for MongoDB. It provides robust data modeling, validation, query building, and middleware support, simplifying the interaction process with MongoDB.

Data Modeling and Schema Definition

At the core of Mongoose is the Schema, which defines the structure of documents, field types, and validation rules. Schemas allow precise control over the shape and behavior of data. For example:

const mongoose = require('mongoose');
const { Schema } = mongoose;

const userSchema = new Schema({
  username: {
    type: String,
    required: true,
    unique: true,
    minlength: 3,
    maxlength: 20
  },
  email: {
    type: String,
    required: true,
    match: /.+\@.+\..+/
  },
  age: {
    type: Number,
    min: 18,
    max: 120
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  isActive: Boolean,
  hobbies: [String]
});

Schemas support various data types:

  • String, Number, Boolean, Date
  • Buffer (binary data)
  • Mixed (any type)
  • ObjectId (references to other documents)
  • Array (arrays of specific types)

Models and Document Operations

Once a Schema is compiled into a Model, you can perform CRUD operations:

const User = mongoose.model('User', userSchema);

// Create a document
const newUser = new User({
  username: 'devuser',
  email: 'dev@example.com',
  age: 25
});

// Save the document
newUser.save()
  .then(doc => console.log(doc))
  .catch(err => console.error(err));

// Query documents
User.findOne({ username: 'devuser' })
  .then(user => console.log(user))
  .catch(err => console.error(err));

Models provide a rich set of query methods:

  • find(): Query multiple documents
  • findOne(): Query a single document
  • findById(): Query by ID
  • countDocuments(): Count documents
  • updateOne()/updateMany(): Update documents
  • deleteOne()/deleteMany(): Delete documents

Advanced Query Features

Mongoose's query builder supports chaining and a variety of operators:

// Chained queries
User.find()
  .where('age').gte(18).lte(30)
  .where('isActive').equals(true)
  .sort('-createdAt')
  .limit(10)
  .select('username email')
  .exec()
  .then(users => console.log(users));

Supported operators include:

  • Comparison: $gt, $gte, $lt, $lte, $ne
  • Logical: $and, $or, $not, $nor
  • Array: $in, $nin, $all
  • Element: $exists, $type

Data Validation

Mongoose offers powerful built-in validators:

const productSchema = new Schema({
  name: { type: String, required: true },
  price: {
    type: Number,
    required: true,
    validate: {
      validator: function(v) {
        return v > 0;
      },
      message: props => `${props.value} is not a valid price`
    }
  },
  category: {
    type: String,
    enum: ['electronics', 'clothing', 'food']
  }
});

Validation types include:

  • Required: required
  • String: minlength, maxlength, match (regex)
  • Number: min, max
  • Enum: enum
  • Custom: validate

Middleware (Hooks)

Mongoose supports middleware to execute logic before or after specific operations:

userSchema.pre('save', function(next) {
  if (this.isModified('password')) {
    this.password = hashPassword(this.password);
  }
  next();
});

userSchema.post('save', function(doc, next) {
  sendWelcomeEmail(doc.email);
  next();
});

Supported hook types:

  • Document middleware: validate, save, remove
  • Query middleware: count, find, findOne, update, delete
  • Aggregate middleware: aggregate
  • Model middleware: insertMany

Virtual Properties

Virtual properties are not stored in the database but can be accessed like regular fields:

userSchema.virtual('fullName').get(function() {
  return `${this.firstName} ${this.lastName}`;
});

userSchema.virtual('fullName').set(function(name) {
  const [firstName, lastName] = name.split(' ');
  this.firstName = firstName;
  this.lastName = lastName;
});

Population (References)

Mongoose can automatically populate document references:

const storySchema = new Schema({
  author: { type: Schema.Types.ObjectId, ref: 'User' },
  title: String
});

Story.findOne({ title: 'Mongoose Guide' })
  .populate('author')
  .exec()
  .then(story => console.log(story.author.username));

Population options include:

  • Selecting fields: populate('field', 'name email')
  • Nested population: populate({ path: 'field', populate: { path: 'nested' } })
  • Conditional population: populate({ path: 'field', match: { age: { $gt: 18 } } })

Plugin System

Mongoose supports extending functionality via plugins:

function timestampPlugin(schema) {
  schema.add({ 
    createdAt: Date,
    updatedAt: Date
  });

  schema.pre('save', function(next) {
    const now = new Date();
    this.updatedAt = now;
    if (!this.createdAt) {
      this.createdAt = now;
    }
    next();
  });
}

userSchema.plugin(timestampPlugin);

Popular plugins:

  • mongoose-autopopulate: Auto-populate references
  • mongoose-paginate: Pagination support
  • mongoose-unique-validator: Uniqueness validation
  • mongoose-timestamp: Auto-timestamps

Transaction Support

Mongoose supports MongoDB transactions:

const session = await mongoose.startSession();
session.startTransaction();

try {
  const user = await User.create([{ username: 'txuser' }], { session });
  await Account.create([{ userId: user[0]._id, balance: 100 }], { session });
  await session.commitTransaction();
} catch (error) {
  await session.abortTransaction();
  throw error;
} finally {
  session.endSession();
}

Performance Optimization

Mongoose offers multiple performance optimization techniques:

  1. Query optimization:
// Select only required fields
User.find().select('username email');

// Use lean() for plain JS objects
User.find().lean();
  1. Bulk operations:
// Bulk insert
User.insertMany([user1, user2, user3]);

// Bulk update
User.updateMany({ isActive: false }, { $set: { status: 'inactive' } });
  1. Index support:
userSchema.index({ username: 1 }, { unique: true });
userSchema.index({ email: 1, status: 1 });

Aggregation Framework Integration

Mongoose fully supports MongoDB's aggregation framework:

Order.aggregate([
  { $match: { status: 'completed' } },
  { $group: {
    _id: '$customer',
    total: { $sum: '$amount' },
    count: { $sum: 1 }
  }},
  { $sort: { total: -1 } },
  { $limit: 10 }
]);

Multiple Database Connections

Mongoose supports connecting to multiple databases simultaneously:

const conn1 = mongoose.createConnection('mongodb://localhost/db1');
const conn2 = mongoose.createConnection('mongodb://localhost/db2');

const Model1 = conn1.model('Model', schema);
const Model2 = conn2.model('Model', schema);

Type Casting and Custom Types

Mongoose supports custom types and type casting:

function toLower(v) {
  return v.toLowerCase();
}

const emailSchema = new Schema({
  email: {
    type: String,
    set: toLower
  }
});

// Custom type
class Money extends mongoose.SchemaType {
  constructor(key, options) {
    super(key, options, 'Money');
  }
  cast(val) {
    return parseFloat(val).toFixed(2);
  }
}

mongoose.Schema.Types.Money = Money;

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.