MongoDB集成 #

一、MongoDB简介 #

1.1 什么是MongoDB? #

MongoDB是一个基于文档的NoSQL数据库,使用JSON格式存储数据,具有高性能、高可用性和易扩展性。

1.2 特点 #

  • 文档存储,灵活的数据结构
  • 支持索引,查询性能高
  • 支持复制和分片
  • 丰富的查询语言

1.3 安装 #

bash
npm install mongoose

二、连接MongoDB #

2.1 基本连接 #

javascript
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/myapp')
    .then(() => console.log('连接成功'))
    .catch(err => console.error('连接失败:', err));

2.2 连接选项 #

javascript
mongoose.connect('mongodb://localhost:27017/myapp', {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    serverSelectionTimeoutMS: 5000,
    maxPoolSize: 10
});

2.3 连接事件 #

javascript
mongoose.connection.on('connected', () => {
    console.log('数据库连接成功');
});

mongoose.connection.on('error', (err) => {
    console.error('数据库连接错误:', err);
});

mongoose.connection.on('disconnected', () => {
    console.log('数据库连接断开');
});

process.on('SIGINT', async () => {
    await mongoose.connection.close();
    process.exit(0);
});

2.4 配置文件 #

config/database.js:

javascript
const mongoose = require('mongoose');

const connectDB = async () => {
    try {
        const conn = await mongoose.connect(process.env.DATABASE_URL);
        console.log(`数据库连接成功: ${conn.connection.host}`);
    } catch (error) {
        console.error('数据库连接失败:', error.message);
        process.exit(1);
    }
};

module.exports = connectDB;

三、Schema定义 #

3.1 基本Schema #

javascript
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: [true, '请输入用户名'],
        trim: true,
        maxlength: [50, '用户名不能超过50个字符']
    },
    email: {
        type: String,
        required: [true, '请输入邮箱'],
        unique: true,
        lowercase: true,
        match: [/^\S+@\S+\.\S+$/, '请输入有效的邮箱地址']
    },
    password: {
        type: String,
        required: [true, '请输入密码'],
        minlength: [6, '密码至少6个字符'],
        select: false
    },
    age: {
        type: Number,
        min: [0, '年龄不能小于0'],
        max: [150, '年龄不能大于150']
    },
    role: {
        type: String,
        enum: ['user', 'admin'],
        default: 'user'
    },
    isActive: {
        type: Boolean,
        default: true
    },
    createdAt: {
        type: Date,
        default: Date.now
    }
});

module.exports = mongoose.model('User', userSchema);

3.2 数据类型 #

类型 说明
String 字符串
Number 数字
Date 日期
Boolean 布尔值
Object 对象
Array 数组
ObjectId 文档ID
Buffer 二进制数据
Decimal128 高精度数字

3.3 嵌套文档 #

javascript
const orderSchema = new mongoose.Schema({
    orderNumber: String,
    customer: {
        name: String,
        email: String,
        address: {
            street: String,
            city: String,
            zipCode: String
        }
    },
    items: [{
        product: String,
        quantity: Number,
        price: Number
    }]
});

3.4 引用关系 #

javascript
const postSchema = new mongoose.Schema({
    title: String,
    content: String,
    author: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    comments: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Comment'
    }]
});

const commentSchema = new mongoose.Schema({
    content: String,
    author: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    post: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Post'
    }
});

四、模型方法 #

4.1 实例方法 #

javascript
userSchema.methods.comparePassword = async function(password) {
    return await bcrypt.compare(password, this.password);
};

userSchema.methods.getFullName = function() {
    return `${this.firstName} ${this.lastName}`;
};

const user = await User.findById(id);
const isMatch = await user.comparePassword('password123');

4.2 静态方法 #

javascript
userSchema.statics.findByEmail = function(email) {
    return this.findOne({ email });
};

userSchema.statics.findActive = function() {
    return this.find({ isActive: true });
};

const user = await User.findByEmail('test@example.com');
const activeUsers = await User.findActive();

4.3 虚拟属性 #

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

userSchema.virtual('passwordConfirmation')
    .set(function(value) {
        this._passwordConfirmation = value;
    });

userSchema.set('toJSON', { virtuals: true });
userSchema.set('toObject', { virtuals: true });

4.4 中间件 #

javascript
userSchema.pre('save', async function(next) {
    if (!this.isModified('password')) {
        return next();
    }
    this.password = await bcrypt.hash(this.password, 10);
    next();
});

userSchema.post('save', function(doc) {
    console.log('用户保存成功:', doc._id);
});

userSchema.pre('remove', function(next) {
    Post.deleteMany({ author: this._id }).exec();
    next();
});

五、CRUD操作 #

5.1 创建文档 #

javascript
const user = await User.create({
    name: '张三',
    email: 'zhangsan@example.com',
    password: 'password123'
});

const user = new User({ name, email, password });
await user.save();

5.2 查询文档 #

javascript
const users = await User.find();
const users = await User.find({ role: 'admin' });
const user = await User.findById(id);
const user = await User.findOne({ email: 'test@example.com' });

const user = await User.findById(id).select('-password');
const users = await User.find().sort({ createdAt: -1 }).limit(10);

5.3 更新文档 #

javascript
await User.findByIdAndUpdate(id, { name: '新名字' });
await User.findByIdAndUpdate(id, { $set: { name: '新名字' } });
await User.updateMany({ role: 'user' }, { isActive: true });

const user = await User.findById(id);
user.name = '新名字';
await user.save();

5.4 删除文档 #

javascript
await User.findByIdAndDelete(id);
await User.deleteOne({ email: 'test@example.com' });
await User.deleteMany({ isActive: false });

六、查询进阶 #

6.1 条件操作符 #

javascript
await User.find({ age: { $gt: 18 } });
await User.find({ age: { $gte: 18, $lte: 60 } });
await User.find({ name: { $in: ['张三', '李四'] } });
await User.find({ name: { $nin: ['张三', '李四'] } });
await User.find({ email: { $exists: true } });

6.2 逻辑操作符 #

javascript
await User.find({ $or: [{ role: 'admin' }, { age: { $gt: 30 } }] });
await User.find({ $and: [{ role: 'user' }, { isActive: true }] });
await User.find({ age: { $not: { $gt: 18 } } });
await User.find({ $nor: [{ role: 'admin' }, { age: { $lt: 18 } }] });

6.3 正则表达式 #

javascript
await User.find({ name: /^张/ });
await User.find({ email: /gmail\.com$/ });
await User.find({ name: { $regex: '张', $options: 'i' } });

6.4 填充引用 #

javascript
const posts = await Post.find().populate('author');
const posts = await Post.find().populate('author', 'name email');
const posts = await Post.find().populate({
    path: 'comments',
    populate: { path: 'author' }
});

6.5 分页查询 #

javascript
const page = 1;
const limit = 10;
const skip = (page - 1) * limit;

const users = await User.find()
    .skip(skip)
    .limit(limit)
    .sort({ createdAt: -1 });

const total = await User.countDocuments();
const totalPages = Math.ceil(total / limit);

七、索引 #

7.1 创建索引 #

javascript
userSchema.index({ email: 1 });
userSchema.index({ email: 1 }, { unique: true });
userSchema.index({ name: 1, age: -1 });
userSchema.index({ content: 'text' });

7.2 索引类型 #

类型 说明
单字段索引 单个字段
复合索引 多个字段
唯一索引 值唯一
文本索引 全文搜索
地理空间索引 位置查询

八、完整示例 #

8.1 模型文件 #

models/User.js:

javascript
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: [true, '请输入用户名'],
        trim: true,
        maxlength: [50, '用户名不能超过50个字符']
    },
    email: {
        type: String,
        required: [true, '请输入邮箱'],
        unique: true,
        lowercase: true,
        match: [/^\S+@\S+\.\S+$/, '请输入有效的邮箱地址']
    },
    password: {
        type: String,
        required: [true, '请输入密码'],
        minlength: [6, '密码至少6个字符'],
        select: false
    },
    role: {
        type: String,
        enum: ['user', 'admin'],
        default: 'user'
    },
    avatar: String,
    isActive: {
        type: Boolean,
        default: true
    }
}, {
    timestamps: true
});

userSchema.pre('save', async function(next) {
    if (!this.isModified('password')) return next();
    this.password = await bcrypt.hash(this.password, 10);
});

userSchema.methods.comparePassword = async function(password) {
    return await bcrypt.compare(password, this.password);
};

userSchema.methods.toJSON = function() {
    const user = this.toObject();
    delete user.password;
    return user;
};

module.exports = mongoose.model('User', userSchema);

8.2 服务文件 #

services/userService.js:

javascript
const User = require('../models/User');

const userService = {
    async findAll(options = {}) {
        const { page = 1, limit = 10, sort = '-createdAt' } = options;
        const skip = (page - 1) * limit;
        
        const [users, total] = await Promise.all([
            User.find().skip(skip).limit(limit).sort(sort),
            User.countDocuments()
        ]);
        
        return {
            users,
            pagination: {
                page,
                limit,
                total,
                totalPages: Math.ceil(total / limit)
            }
        };
    },
    
    async findById(id) {
        return await User.findById(id);
    },
    
    async findByEmail(email) {
        return await User.findOne({ email });
    },
    
    async create(userData) {
        const user = new User(userData);
        return await user.save();
    },
    
    async update(id, userData) {
        return await User.findByIdAndUpdate(
            id,
            { $set: userData },
            { new: true, runValidators: true }
        );
    },
    
    async delete(id) {
        return await User.findByIdAndDelete(id);
    }
};

module.exports = userService;

九、总结 #

MongoDB集成要点:

概念 说明
连接 mongoose.connect()
Schema 定义数据结构
Model 操作数据库
CRUD 增删改查
查询 条件、排序、分页
索引 提升查询性能

下一步,让我们学习MySQL集成!

最后更新:2026-03-28