Mongoose 与 MongoDB 数据库教程

Mongoose 与 MongoDB 数据库教程

介绍

Mongoose 是 Node.js 中最流行的 MongoDB 对象建模工具,它为 MongoDB 提供了 schema-based 的解决方案,包括数据验证、查询构建、业务逻辑钩子等。

前置条件

  • Node.js 安装(建议 v14 或更高版本)
  • MongoDB 数据库(本地或 Atlas 云服务)
  • 基本的 JavaScript/TypeScript 知识

安装与设置

  1. 创建新项目并初始化
bash 复制代码
mkdir mongoose-tutorial
cd mongoose-tutorial
npm init -y
npm install mongoose
  1. 安装开发依赖(可选)
bash 复制代码
npm install typescript ts-node @types/node @types/mongoose --save-dev

连接 MongoDB

  1. 创建连接文件 db.js:
javascript 复制代码
const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    await mongoose.connect('mongodb://localhost:27017/mongoose_tutorial', {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log('MongoDB Connected...');
  } catch (err) {
    console.error('Connection error:', err.message);
    process.exit(1);
  }
};

module.exports = connectDB;
  1. 使用 Atlas 云服务的连接字符串:
javascript 复制代码
mongoose.connect('mongodb+srv://<username>:<password>@cluster0.mongodb.net/mongoose_tutorial?retryWrites=true&w=majority');

定义 Schema 和 Model

  1. 创建用户模型 models/User.js:
javascript 复制代码
const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true,
    unique: true
  },
  password: {
    type: String,
    required: true
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('User', UserSchema);
  1. 创建博客文章模型 models/Post.js:
javascript 复制代码
const mongoose = require('mongoose');

const PostSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true
  },
  content: {
    type: String,
    required: true
  },
  author: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  published: {
    type: Boolean,
    default: false
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('Post', PostSchema);

基本 CRUD 操作

创建记录

javascript 复制代码
const User = require('./models/User');

const createUser = async () => {
  try {
    const user = new User({
      name: 'John Doe',
      email: 'john@example.com',
      password: 'securepassword123'
    });
    
    await user.save();
    console.log('User created:', user);
  } catch (err) {
    console.error('Error creating user:', err.message);
  }
};

查询记录

javascript 复制代码
// 查找所有用户
const users = await User.find();

// 带条件的查询
const activeUsers = await User.find({ active: true });

// 单条查询
const user = await User.findOne({ email: 'john@example.com' });

// 按ID查询
const userById = await User.findById('507f1f77bcf86cd799439011');

更新记录

javascript 复制代码
// 更新单个文档
const updatedUser = await User.findOneAndUpdate(
  { email: 'john@example.com' },
  { name: 'John Smith' },
  { new: true } // 返回更新后的文档
);

// 更新多个文档
const result = await User.updateMany(
  { active: false },
  { active: true }
);

删除记录

javascript 复制代码
// 删除单个文档
const deletedUser = await User.findOneAndDelete({ email: 'john@example.com' });

// 按ID删除
await User.findByIdAndDelete('507f1f77bcf86cd799439011');

// 删除多个文档
await User.deleteMany({ active: false });

高级功能

数据验证

javascript 复制代码
const ProductSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'Product name is required'],
    minlength: [3, 'Name must be at least 3 characters'],
    maxlength: [100, 'Name cannot exceed 100 characters']
  },
  price: {
    type: Number,
    required: true,
    min: [0, 'Price cannot be negative']
  },
  category: {
    type: String,
    enum: ['electronics', 'clothing', 'books', 'other']
  }
});

中间件(钩子)

javascript 复制代码
UserSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  
  try {
    const salt = await bcrypt.genSalt(10);
    this.password = await bcrypt.hash(this.password, salt);
    next();
  } catch (err) {
    next(err);
  }
});

UserSchema.post('save', function(doc, next) {
  console.log('New user was created:', doc);
  next();
});

虚拟属性

javascript 复制代码
UserSchema.virtual('fullName').get(function() {
  return `${this.firstName} ${this.lastName}`;
});

查询构建

javascript 复制代码
const results = await User.find()
  .where('age').gt(18)
  .where('active').equals(true)
  .sort('-createdAt')
  .limit(10)
  .select('name email')
  .exec();

聚合管道

javascript 复制代码
const stats = await User.aggregate([
  { $match: { active: true } },
  { $group: { 
    _id: '$department', 
    total: { $sum: 1 },
    avgAge: { $avg: '$age' }
  }},
  { $sort: { total: -1 } }
]);

填充引用

javascript 复制代码
const posts = await Post.find()
  .populate('author', 'name email')
  .exec();

索引

javascript 复制代码
UserSchema.index({ email: 1 }, { unique: true });
UserSchema.index({ name: 'text' }); // 文本索引

错误处理

javascript 复制代码
try {
  const user = await User.create({ email: 'existing@email.com' });
} catch (err) {
  if (err.code === 11000) {
    console.log('Duplicate key error');
  } else {
    console.log('Other error:', err.message);
  }
}

使用 Mongoose 与 TypeScript

  1. 定义接口:
typescript 复制代码
import { Document, Schema, model } from 'mongoose';

interface IUser extends Document {
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

const UserSchema = new Schema<IUser>({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  createdAt: { type: Date, default: Date.now }
});

const User = model<IUser>('User', UserSchema);

最佳实践

  1. 连接管理:

    • 在应用启动时连接数据库
    • 使用连接池
    • 处理连接错误和断开
  2. Schema 设计:

    • 合理使用引用和嵌入
    • 考虑读写比例
    • 避免大文档
  3. 性能优化:

    • 使用适当的索引
    • 限制返回字段
    • 使用分页
  4. 安全:

    • 不要直接暴露 Mongoose 文档
    • 使用 schema 验证
    • 清理用户输入

示例应用结构

text 复制代码
project/
├── models/
│   ├── User.js
│   ├── Post.js
│   └── index.js
├── db.js
├── services/
│   ├── userService.js
│   └── postService.js
├── controllers/
│   ├── userController.js
│   └── postController.js
└── app.js

总结

Mongoose 为 MongoDB 提供了强大的抽象层,使得在 Node.js 中处理数据更加方便和安全。通过定义 Schema 和 Model,你可以获得数据验证、类型安全、中间件钩子等特性。结合 MongoDB 的灵活性和性能,Mongoose 是构建 Node.js 应用的优秀选择。

相关推荐
超级无敌攻城狮11 分钟前
3 分钟学会!波浪文字动画超详细教程,从 0 到 1 实现「思考中 / 加载中」高级效果
前端
excel1 小时前
用 TensorFlow.js Node 实现猫图像识别(教学版逐步分解)
前端
gnip1 小时前
JavaScript事件流
前端·javascript
赵得C2 小时前
【前端技巧】Element Table 列标题如何优雅添加 Tooltip 提示?
前端·elementui·vue·table组件
wow_DG2 小时前
【Vue2 ✨】Vue2 入门之旅 · 进阶篇(一):响应式原理
前端·javascript·vue.js
weixin_456904272 小时前
UserManagement.vue和Profile.vue详细解释
前端·javascript·vue.js
资深前端之路2 小时前
react 面试题 react 有什么特点?
前端·react.js·面试·前端框架
aaaweiaaaaaa2 小时前
HTML和CSS学习
前端·css·学习·html
秋秋小事2 小时前
React Hooks useContext
前端·javascript·react.js
Jinuss2 小时前
Vue3源码reactivity响应式篇之reactive响应式对象的track与trigger
前端·vue3