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

相关推荐
先吃饱再说6 分钟前
React 组件通信:从 Props 单向传递到自定义事件
前端·react.js·前端框架
swipe11 分钟前
05|(前端转后全栈)不手写一堆 SQL,后端怎么操作数据库?MyBatis-Plus 入门
前端·后端·全栈
swipe16 分钟前
04|(前端转后全栈)前端状态为什么不够用?从页面数据到 MySQL 持久化
前端·后端·全栈
橘子星17 分钟前
别光看教程!手把手拆解 React Todo 项目,一文吃透 5 个核心概念
前端·javascript
大白要努力!23 分钟前
纯前端实现 PDF 加水印工具 —— 零后端、支持中文、实时预览
前端·pdf·html
石小石Orz25 分钟前
TRAE SOLO实战:实现一个桌面3D助手
前端·人工智能
蜡台30 分钟前
使用 uni-popup 实现数据选择器Data-Picker
前端·javascript·html·uniapp·uni-popup·data-picker
拆房老料31 分钟前
BaseMetas FileView 1.2.0 发布:Office/WPS 大文件预览与内存安全优化实测
前端·产品运营·开源软件
blns_yxl35 分钟前
Promise封装Fetch + 重试机制(HTML+JS)
前端·javascript·html
Elastic 中国社区官方博客39 分钟前
使用重新设计的 AutoOps 更快地进行 Elasticsearch 问题排查
大数据·运维·前端·人工智能·elasticsearch·搜索引擎·全文检索