阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 模型(Model)的创建与使用

模型(Model)的创建与使用

作者:陈川 阅读数:25360人阅读 分类: MongoDB

模型(Model)的创建与使用

Mongoose 中的模型是 Schema 的实例,用于与 MongoDB 集合进行交互。模型提供了创建、查询、更新和删除文档的方法,是 Mongoose 的核心功能之一。

定义 Schema

在创建模型之前,需要先定义 Schema。Schema 定义了文档的结构、字段类型、默认值和验证规则等。

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,
    unique: true,
    match: /^\S+@\S+\.\S+$/
  },
  age: {
    type: Number,
    min: 18,
    max: 120
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  isActive: {
    type: Boolean,
    default: true
  }
});

创建模型

使用 mongoose.model() 方法将 Schema 编译为模型。模型名称通常使用单数形式,Mongoose 会自动将其转换为复数形式作为集合名称。

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

也可以指定自定义的集合名称:

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

模型方法

模型提供了多种方法来操作数据库:

创建文档

// 创建单个文档
const newUser = new User({
  username: 'john_doe',
  email: 'john@example.com',
  age: 30
});

newUser.save()
  .then(doc => console.log(doc))
  .catch(err => console.error(err));

// 或者使用 create 方法
User.create({
  username: 'jane_doe',
  email: 'jane@example.com',
  age: 28
})
.then(doc => console.log(doc))
.catch(err => console.error(err));

查询文档

// 查找所有文档
User.find({})
  .then(users => console.log(users))
  .catch(err => console.error(err));

// 带条件查询
User.find({ age: { $gte: 25 } })
  .then(users => console.log(users))
  .catch(err => console.error(err));

// 查找单个文档
User.findOne({ username: 'john_doe' })
  .then(user => console.log(user))
  .catch(err => console.error(err));

// 按 ID 查找
User.findById('507f1f77bcf86cd799439011')
  .then(user => console.log(user))
  .catch(err => console.error(err));

更新文档

// 更新单个文档
User.updateOne(
  { username: 'john_doe' },
  { $set: { age: 31 } }
)
.then(result => console.log(result))
.catch(err => console.error(err));

// 更新多个文档
User.updateMany(
  { isActive: true },
  { $set: { lastLogin: new Date() } }
)
.then(result => console.log(result))
.catch(err => console.error(err));

// findByIdAndUpdate
User.findByIdAndUpdate(
  '507f1f77bcf86cd799439011',
  { $inc: { age: 1 } },
  { new: true } // 返回更新后的文档
)
.then(user => console.log(user))
.catch(err => console.error(err));

删除文档

// 删除单个文档
User.deleteOne({ username: 'john_doe' })
  .then(result => console.log(result))
  .catch(err => console.error(err));

// 删除多个文档
User.deleteMany({ isActive: false })
  .then(result => console.log(result))
  .catch(err => console.error(err));

// findByIdAndDelete
User.findByIdAndDelete('507f1f77bcf86cd799439011')
  .then(user => console.log(user))
  .catch(err => console.error(err));

静态方法

可以在模型上定义静态方法:

userSchema.statics.findByUsername = function(username) {
  return this.find({ username: new RegExp(username, 'i') });
};

// 使用静态方法
User.findByUsername('john')
  .then(users => console.log(users))
  .catch(err => console.error(err));

实例方法

也可以在文档实例上定义方法:

userSchema.methods.getProfile = function() {
  return {
    username: this.username,
    email: this.email,
    memberSince: this.createdAt.toLocaleDateString()
  };
};

// 使用实例方法
User.findOne({ username: 'john_doe' })
  .then(user => {
    if (user) {
      console.log(user.getProfile());
    }
  })
  .catch(err => console.error(err));

查询构建器

Mongoose 提供了链式查询构建器:

User.find()
  .where('age').gte(18).lte(65)
  .where('isActive').equals(true)
  .sort('-createdAt')
  .limit(10)
  .select('username email age')
  .exec()
  .then(users => console.log(users))
  .catch(err => console.error(err));

中间件

可以在模型操作前后添加中间件:

// 保存前预处理
userSchema.pre('save', function(next) {
  if (this.isModified('email')) {
    this.email = this.email.toLowerCase();
  }
  next();
});

// 查询后处理
userSchema.post('find', function(docs) {
  console.log(`Found ${docs.length} users`);
});

虚拟属性

虚拟属性不会存入数据库,但可以像普通属性一样访问:

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;
});

const user = new User();
user.fullName = 'John Doe';
console.log(user.firstName); // 'John'
console.log(user.lastName);  // 'Doe'
console.log(user.fullName);  // 'John Doe'

索引

可以在 Schema 中定义索引:

userSchema.index({ username: 1 }, { unique: true });
userSchema.index({ email: 1 }, { unique: true });
userSchema.index({ age: 1, isActive: 1 });

模型验证

Mongoose 提供了内置验证器:

const productSchema = new Schema({
  name: {
    type: String,
    required: [true, '产品名称是必填项'],
    trim: true
  },
  price: {
    type: Number,
    required: true,
    min: [0, '价格不能小于0']
  },
  stock: {
    type: Number,
    default: 0,
    validate: {
      validator: Number.isInteger,
      message: '库存必须是整数'
    }
  },
  category: {
    type: String,
    enum: {
      values: ['电子产品', '服装', '食品'],
      message: '无效的商品类别'
    }
  }
});

关联关系

Mongoose 支持文档间的关联:

const orderSchema = new Schema({
  user: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  products: [{
    product: {
      type: Schema.Types.ObjectId,
      ref: 'Product'
    },
    quantity: Number
  }],
  totalAmount: Number
});

// 填充关联数据
Order.findById(orderId)
  .populate('user')
  .populate('products.product')
  .exec()
  .then(order => console.log(order))
  .catch(err => console.error(err));

分页查询

实现分页查询的常见模式:

const getUsers = async (page = 1, limit = 10) => {
  const skip = (page - 1) * limit;
  
  const [users, total] = await Promise.all([
    User.find().skip(skip).limit(limit),
    User.countDocuments()
  ]);
  
  return {
    users,
    total,
    pages: Math.ceil(total / limit),
    currentPage: page
  };
};

事务处理

Mongoose 支持 MongoDB 事务:

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

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

聚合管道

使用聚合框架进行复杂查询:

User.aggregate([
  { $match: { isActive: true } },
  { $group: {
    _id: '$ageGroup',
    count: { $sum: 1 },
    averageAge: { $avg: '$age' }
  }},
  { $sort: { count: -1 } }
])
.then(results => console.log(results))
.catch(err => console.error(err));

性能优化

一些模型使用中的性能优化技巧:

  1. 只查询需要的字段:
User.find().select('username email -_id')
  1. 使用 lean() 返回普通 JavaScript 对象:
User.find().lean()
  1. 合理使用索引:
userSchema.index({ createdAt: -1 });
  1. 批量操作时使用 bulkWrite:
User.bulkWrite([
  { insertOne: { document: { username: 'user1' } } },
  { updateOne: { filter: { username: 'user2' }, update: { $set: { age: 30 } } } }
])

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

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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌