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 应用的优秀选择。

相关推荐
chao_78944 分钟前
frame 与新窗口切换操作【selenium 】
前端·javascript·css·selenium·测试工具·自动化·html
天蓝色的鱼鱼1 小时前
从零实现浏览器摄像头控制与视频录制:基于原生 JavaScript 的完整指南
前端·javascript
三原1 小时前
7000块帮朋友做了2个小程序加一个后台管理系统,值不值?
前端·vue.js·微信小程序
popoxf1 小时前
在新版本的微信开发者工具中使用npm包
前端·npm·node.js
爱编程的喵2 小时前
React Router Dom 初步:从传统路由到现代前端导航
前端·react.js
每天吃饭的羊2 小时前
react中为啥使用剪头函数
前端·javascript·react.js
Nicholas683 小时前
Flutter帧定义与60-120FPS机制
前端
多啦C梦a3 小时前
【适合小白篇】什么是 SPA?前端路由到底在路由个啥?我来给你聊透!
前端·javascript·架构
薛定谔的算法3 小时前
《长安的荔枝·事件流版》——一颗荔枝引发的“冒泡惨案”
前端·javascript·编程语言
中微子3 小时前
CSS 的 position 你真的理解了吗?
前端·css