Skip to content

Node.js MongoDB集成2024:NoSQL数据库开发完整指南

📊 SEO元描述:2024年最新Node.js MongoDB集成教程,详解Mongoose ODM、数据建模、CRUD操作。包含完整项目示例,适合Node.js开发者掌握NoSQL数据库开发技术。

核心关键词:Node.js MongoDB集成2024、Mongoose ODM使用、MongoDB数据建模、NoSQL数据库操作、Node.js数据库连接

长尾关键词:Node.js怎么连接MongoDB、Mongoose模型定义、MongoDB CRUD操作、NoSQL数据库设计、Node.js数据库最佳实践


📚 MongoDB集成学习目标与核心收获

通过本节Node.js MongoDB集成教程,你将系统性掌握:

  • MongoDB基础概念:理解NoSQL数据库的特点和MongoDB的核心优势
  • 数据库连接配置:掌握Node.js连接MongoDB的多种方式和最佳实践
  • Mongoose ODM使用:学会使用Mongoose进行对象文档映射和数据操作
  • 数据模型设计:掌握MongoDB文档结构设计和关系建模技巧
  • CRUD操作详解:实现完整的创建、读取、更新、删除数据操作
  • 高级查询技巧:学会复杂查询、聚合管道和索引优化

🎯 适合人群

  • Node.js进阶学习者的NoSQL数据库技能提升
  • 全栈开发者的后端数据存储技术掌握
  • 数据库开发工程师的MongoDB实战应用
  • Web开发者的现代数据库技术学习

🌟 MongoDB是什么?为什么选择NoSQL数据库?

MongoDB是什么?这是现代Web开发中的重要问题。MongoDB是一个基于文档的NoSQL数据库,以其灵活性、可扩展性和开发效率而闻名,也是现代Web应用的理想数据存储解决方案。

MongoDB的核心优势

  • 🎯 文档存储:使用JSON-like的BSON格式,与JavaScript对象完美匹配
  • 🔧 灵活模式:无需预定义表结构,支持动态字段添加
  • 💡 水平扩展:内置分片支持,轻松处理大规模数据
  • 📚 丰富查询:支持复杂查询、聚合和全文搜索
  • 🚀 高性能:内存映射存储引擎,读写性能优秀

💡 设计理念:MongoDB让开发者能够以更自然的方式存储和操作数据

MongoDB连接配置

让我们从MongoDB的连接配置开始:

javascript
// 🎉 MongoDB连接配置详解
const mongoose = require('mongoose');
const { MongoClient } = require('mongodb');

// 1. 基础连接配置
class DatabaseConnection {
    constructor() {
        this.mongooseConnection = null;
        this.nativeConnection = null;
        this.connectionString = process.env.MONGODB_URI || 'mongodb://localhost:27017/nodeapp';
    }

    // Mongoose连接(推荐方式)
    async connectWithMongoose() {
        try {
            const options = {
                useNewUrlParser: true,
                useUnifiedTopology: true,
                maxPoolSize: 10,        // 连接池最大连接数
                serverSelectionTimeoutMS: 5000, // 服务器选择超时
                socketTimeoutMS: 45000, // Socket超时
                family: 4,              // 使用IPv4
                bufferCommands: false,  // 禁用mongoose缓冲
                bufferMaxEntries: 0     // 禁用mongoose缓冲
            };

            this.mongooseConnection = await mongoose.connect(this.connectionString, options);
            
            console.log('✅ Mongoose连接成功');
            console.log(`📍 数据库: ${mongoose.connection.name}`);
            console.log(`🌐 主机: ${mongoose.connection.host}:${mongoose.connection.port}`);
            
            // 连接事件监听
            this.setupMongooseEventListeners();
            
            return this.mongooseConnection;
        } catch (error) {
            console.error('❌ Mongoose连接失败:', error);
            throw error;
        }
    }

    // 原生MongoDB驱动连接
    async connectWithNativeDriver() {
        try {
            const options = {
                maxPoolSize: 10,
                serverSelectionTimeoutMS: 5000,
                socketTimeoutMS: 45000,
                family: 4
            };

            this.nativeConnection = new MongoClient(this.connectionString, options);
            await this.nativeConnection.connect();
            
            console.log('✅ 原生MongoDB驱动连接成功');
            
            // 测试连接
            await this.nativeConnection.db().admin().ping();
            console.log('🏓 数据库ping测试成功');
            
            return this.nativeConnection;
        } catch (error) {
            console.error('❌ 原生驱动连接失败:', error);
            throw error;
        }
    }

    // 设置Mongoose事件监听器
    setupMongooseEventListeners() {
        const db = mongoose.connection;

        db.on('connected', () => {
            console.log('🔗 Mongoose已连接到MongoDB');
        });

        db.on('error', (error) => {
            console.error('💥 Mongoose连接错误:', error);
        });

        db.on('disconnected', () => {
            console.log('🔌 Mongoose已断开连接');
        });

        // 应用终止时关闭连接
        process.on('SIGINT', async () => {
            await db.close();
            console.log('👋 Mongoose连接已关闭');
            process.exit(0);
        });
    }

    // 连接状态检查
    getConnectionStatus() {
        const states = {
            0: 'disconnected',
            1: 'connected',
            2: 'connecting',
            3: 'disconnecting'
        };

        return {
            mongoose: {
                state: states[mongoose.connection.readyState],
                host: mongoose.connection.host,
                port: mongoose.connection.port,
                name: mongoose.connection.name
            },
            native: this.nativeConnection ? 'connected' : 'disconnected'
        };
    }

    // 优雅关闭连接
    async closeConnections() {
        try {
            if (this.mongooseConnection) {
                await mongoose.connection.close();
                console.log('🔒 Mongoose连接已关闭');
            }

            if (this.nativeConnection) {
                await this.nativeConnection.close();
                console.log('🔒 原生驱动连接已关闭');
            }
        } catch (error) {
            console.error('❌ 关闭连接时出错:', error);
        }
    }
}

// 使用示例
const dbConnection = new DatabaseConnection();

// 启动连接
async function initializeDatabase() {
    try {
        await dbConnection.connectWithMongoose();
        
        // 连接状态检查
        const status = dbConnection.getConnectionStatus();
        console.log('📊 连接状态:', status);
        
    } catch (error) {
        console.error('💥 数据库初始化失败:', error);
        process.exit(1);
    }
}

// 环境配置
const config = {
    development: {
        mongodb: {
            uri: 'mongodb://localhost:27017/nodeapp_dev',
            options: {
                useNewUrlParser: true,
                useUnifiedTopology: true
            }
        }
    },
    test: {
        mongodb: {
            uri: 'mongodb://localhost:27017/nodeapp_test',
            options: {
                useNewUrlParser: true,
                useUnifiedTopology: true
            }
        }
    },
    production: {
        mongodb: {
            uri: process.env.MONGODB_URI,
            options: {
                useNewUrlParser: true,
                useUnifiedTopology: true,
                maxPoolSize: 20,
                serverSelectionTimeoutMS: 5000,
                socketTimeoutMS: 45000,
                ssl: true,
                sslValidate: true
            }
        }
    }
};

// 根据环境获取配置
function getConfig() {
    const env = process.env.NODE_ENV || 'development';
    return config[env];
}

module.exports = {
    DatabaseConnection,
    initializeDatabase,
    getConfig
};

console.log('MongoDB连接配置完成');

MongoDB连接要点

  • 连接池:配置合适的连接池大小,提高并发性能
  • 超时设置:设置合理的连接和操作超时时间
  • 错误处理:实现完善的连接错误处理和重连机制
  • 环境配置:区分开发、测试、生产环境的数据库配置

Mongoose ODM基础使用

Schema定义和Model创建

javascript
// 🔧 Mongoose ODM详解
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

// 1. 基础Schema定义
const userSchema = new mongoose.Schema({
    username: {
        type: String,
        required: [true, '用户名是必需的'],
        unique: true,
        trim: true,
        minlength: [3, '用户名至少3个字符'],
        maxlength: [30, '用户名最多30个字符'],
        match: [/^[a-zA-Z0-9_]+$/, '用户名只能包含字母、数字和下划线']
    },
    email: {
        type: String,
        required: [true, '邮箱是必需的'],
        unique: true,
        lowercase: true,
        trim: true,
        match: [/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/, '邮箱格式不正确']
    },
    password: {
        type: String,
        required: [true, '密码是必需的'],
        minlength: [6, '密码至少6个字符'],
        select: false // 默认查询时不返回密码字段
    },
    profile: {
        firstName: {
            type: String,
            trim: true,
            maxlength: [50, '名字最多50个字符']
        },
        lastName: {
            type: String,
            trim: true,
            maxlength: [50, '姓氏最多50个字符']
        },
        avatar: {
            type: String,
            default: null
        },
        bio: {
            type: String,
            maxlength: [500, '个人简介最多500个字符']
        },
        dateOfBirth: {
            type: Date,
            validate: {
                validator: function(value) {
                    return value < new Date();
                },
                message: '出生日期不能是未来时间'
            }
        }
    },
    role: {
        type: String,
        enum: {
            values: ['user', 'admin', 'moderator'],
            message: '角色必须是 user、admin 或 moderator'
        },
        default: 'user'
    },
    isActive: {
        type: Boolean,
        default: true
    },
    lastLogin: {
        type: Date,
        default: null
    },
    loginAttempts: {
        type: Number,
        default: 0
    },
    lockUntil: {
        type: Date,
        default: null
    },
    emailVerified: {
        type: Boolean,
        default: false
    },
    emailVerificationToken: {
        type: String,
        default: null
    },
    passwordResetToken: {
        type: String,
        default: null
    },
    passwordResetExpires: {
        type: Date,
        default: null
    }
}, {
    timestamps: true, // 自动添加createdAt和updatedAt
    toJSON: { 
        virtuals: true,
        transform: function(doc, ret) {
            delete ret.password;
            delete ret.emailVerificationToken;
            delete ret.passwordResetToken;
            delete ret.__v;
            return ret;
        }
    },
    toObject: { virtuals: true }
});

// 2. 虚拟字段
userSchema.virtual('fullName').get(function() {
    if (this.profile.firstName && this.profile.lastName) {
        return `${this.profile.firstName} ${this.profile.lastName}`;
    }
    return this.username;
});

userSchema.virtual('isLocked').get(function() {
    return !!(this.lockUntil && this.lockUntil > Date.now());
});

userSchema.virtual('age').get(function() {
    if (this.profile.dateOfBirth) {
        const today = new Date();
        const birthDate = new Date(this.profile.dateOfBirth);
        let age = today.getFullYear() - birthDate.getFullYear();
        const monthDiff = today.getMonth() - birthDate.getMonth();
        
        if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
            age--;
        }
        
        return age;
    }
    return null;
});

// 3. 索引定义
userSchema.index({ email: 1 }, { unique: true });
userSchema.index({ username: 1 }, { unique: true });
userSchema.index({ 'profile.firstName': 1, 'profile.lastName': 1 });
userSchema.index({ createdAt: -1 });
userSchema.index({ lastLogin: -1 });

// 4. 中间件(Middleware)
// 保存前中间件 - 密码加密
userSchema.pre('save', async function(next) {
    // 只有密码被修改时才加密
    if (!this.isModified('password')) return next();
    
    try {
        const salt = await bcrypt.genSalt(12);
        this.password = await bcrypt.hash(this.password, salt);
        next();
    } catch (error) {
        next(error);
    }
});

// 保存前中间件 - 邮箱验证令牌
userSchema.pre('save', function(next) {
    if (this.isNew && !this.emailVerificationToken) {
        this.emailVerificationToken = require('crypto').randomBytes(32).toString('hex');
    }
    next();
});

// 查询中间件 - 只返回活跃用户
userSchema.pre(/^find/, function(next) {
    // 默认只查询活跃用户,除非明确指定
    if (!this.getQuery().isActive) {
        this.find({ isActive: { $ne: false } });
    }
    next();
});

// 5. 实例方法
userSchema.methods.comparePassword = async function(candidatePassword) {
    try {
        return await bcrypt.compare(candidatePassword, this.password);
    } catch (error) {
        throw error;
    }
};

userSchema.methods.incrementLoginAttempts = function() {
    // 如果已经锁定且锁定时间已过,重置计数器
    if (this.lockUntil && this.lockUntil < Date.now()) {
        return this.updateOne({
            $unset: { lockUntil: 1 },
            $set: { loginAttempts: 1 }
        });
    }
    
    const updates = { $inc: { loginAttempts: 1 } };
    
    // 如果达到最大尝试次数且未锁定,则锁定账户
    if (this.loginAttempts + 1 >= 5 && !this.isLocked) {
        updates.$set = { lockUntil: Date.now() + 2 * 60 * 60 * 1000 }; // 锁定2小时
    }
    
    return this.updateOne(updates);
};

userSchema.methods.resetLoginAttempts = function() {
    return this.updateOne({
        $unset: { loginAttempts: 1, lockUntil: 1 }
    });
};

userSchema.methods.generatePasswordResetToken = function() {
    const resetToken = require('crypto').randomBytes(32).toString('hex');
    
    this.passwordResetToken = require('crypto')
        .createHash('sha256')
        .update(resetToken)
        .digest('hex');
    
    this.passwordResetExpires = Date.now() + 10 * 60 * 1000; // 10分钟有效
    
    return resetToken;
};

// 6. 静态方法
userSchema.statics.findByEmail = function(email) {
    return this.findOne({ email: email.toLowerCase() });
};

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

userSchema.statics.getUserStats = function() {
    return this.aggregate([
        {
            $group: {
                _id: '$role',
                count: { $sum: 1 },
                activeCount: {
                    $sum: { $cond: [{ $eq: ['$isActive', true] }, 1, 0] }
                }
            }
        },
        {
            $project: {
                role: '$_id',
                count: 1,
                activeCount: 1,
                inactiveCount: { $subtract: ['$count', '$activeCount'] }
            }
        }
    ]);
};

// 创建模型
const User = mongoose.model('User', userSchema);

module.exports = User;

console.log('Mongoose用户模型定义完成');

Mongoose ODM要点

  • 🎯 Schema定义:定义数据结构、验证规则和约束条件
  • 🎯 中间件:在保存、查询等操作前后执行自定义逻辑
  • 🎯 虚拟字段:计算字段,不存储在数据库中
  • 🎯 索引优化:为常用查询字段创建索引
  • 🎯 实例和静态方法:扩展模型功能

MongoDB CRUD操作详解

完整的数据操作实现

javascript
// 🚀 MongoDB CRUD操作详解
const User = require('./models/User');
const mongoose = require('mongoose');

class UserService {
    // 1. 创建用户 (Create)
    async createUser(userData) {
        try {
            // 数据验证和清理
            const cleanedData = this.sanitizeUserData(userData);

            // 检查用户是否已存在
            const existingUser = await User.findOne({
                $or: [
                    { email: cleanedData.email },
                    { username: cleanedData.username }
                ]
            });

            if (existingUser) {
                throw new Error('用户名或邮箱已存在');
            }

            // 创建新用户
            const user = new User(cleanedData);
            const savedUser = await user.save();

            console.log('✅ 用户创建成功:', savedUser.username);
            return savedUser;

        } catch (error) {
            if (error.name === 'ValidationError') {
                const errors = Object.values(error.errors).map(err => err.message);
                throw new Error(`数据验证失败: ${errors.join(', ')}`);
            }
            throw error;
        }
    }

    // 2. 读取用户 (Read)
    async getUserById(userId) {
        try {
            if (!mongoose.Types.ObjectId.isValid(userId)) {
                throw new Error('无效的用户ID格式');
            }

            const user = await User.findById(userId)
                .populate('profile')
                .select('+lastLogin'); // 包含通常不返回的字段

            if (!user) {
                throw new Error('用户不存在');
            }

            return user;
        } catch (error) {
            throw error;
        }
    }

    async getUserByEmail(email) {
        try {
            const user = await User.findByEmail(email);
            return user;
        } catch (error) {
            throw error;
        }
    }

    async getUsers(options = {}) {
        try {
            const {
                page = 1,
                limit = 10,
                sort = '-createdAt',
                filter = {},
                search = ''
            } = options;

            // 构建查询条件
            let query = { ...filter };

            // 搜索功能
            if (search) {
                query.$or = [
                    { username: { $regex: search, $options: 'i' } },
                    { email: { $regex: search, $options: 'i' } },
                    { 'profile.firstName': { $regex: search, $options: 'i' } },
                    { 'profile.lastName': { $regex: search, $options: 'i' } }
                ];
            }

            // 分页计算
            const skip = (page - 1) * limit;

            // 执行查询
            const [users, total] = await Promise.all([
                User.find(query)
                    .sort(sort)
                    .skip(skip)
                    .limit(limit)
                    .populate('profile'),
                User.countDocuments(query)
            ]);

            return {
                users,
                pagination: {
                    page,
                    limit,
                    total,
                    pages: Math.ceil(total / limit),
                    hasNext: page * limit < total,
                    hasPrev: page > 1
                }
            };
        } catch (error) {
            throw error;
        }
    }

    // 3. 更新用户 (Update)
    async updateUser(userId, updateData) {
        try {
            if (!mongoose.Types.ObjectId.isValid(userId)) {
                throw new Error('无效的用户ID格式');
            }

            // 清理更新数据
            const cleanedData = this.sanitizeUserData(updateData, true);

            // 如果更新邮箱或用户名,检查唯一性
            if (cleanedData.email || cleanedData.username) {
                const conflictQuery = {
                    _id: { $ne: userId },
                    $or: []
                };

                if (cleanedData.email) {
                    conflictQuery.$or.push({ email: cleanedData.email });
                }
                if (cleanedData.username) {
                    conflictQuery.$or.push({ username: cleanedData.username });
                }

                const conflictUser = await User.findOne(conflictQuery);
                if (conflictUser) {
                    throw new Error('用户名或邮箱已被其他用户使用');
                }
            }

            // 执行更新
            const updatedUser = await User.findByIdAndUpdate(
                userId,
                { $set: cleanedData },
                {
                    new: true,           // 返回更新后的文档
                    runValidators: true, // 运行验证器
                    context: 'query'     // 验证器上下文
                }
            );

            if (!updatedUser) {
                throw new Error('用户不存在');
            }

            console.log('✅ 用户更新成功:', updatedUser.username);
            return updatedUser;

        } catch (error) {
            if (error.name === 'ValidationError') {
                const errors = Object.values(error.errors).map(err => err.message);
                throw new Error(`数据验证失败: ${errors.join(', ')}`);
            }
            throw error;
        }
    }

    async updateUserProfile(userId, profileData) {
        try {
            const updateData = {};

            // 构建嵌套更新对象
            Object.keys(profileData).forEach(key => {
                updateData[`profile.${key}`] = profileData[key];
            });

            const updatedUser = await User.findByIdAndUpdate(
                userId,
                { $set: updateData },
                { new: true, runValidators: true }
            );

            if (!updatedUser) {
                throw new Error('用户不存在');
            }

            return updatedUser;
        } catch (error) {
            throw error;
        }
    }

    // 4. 删除用户 (Delete)
    async deleteUser(userId) {
        try {
            if (!mongoose.Types.ObjectId.isValid(userId)) {
                throw new Error('无效的用户ID格式');
            }

            const deletedUser = await User.findByIdAndDelete(userId);

            if (!deletedUser) {
                throw new Error('用户不存在');
            }

            console.log('✅ 用户删除成功:', deletedUser.username);
            return deletedUser;
        } catch (error) {
            throw error;
        }
    }

    async softDeleteUser(userId) {
        try {
            const updatedUser = await User.findByIdAndUpdate(
                userId,
                {
                    $set: {
                        isActive: false,
                        deletedAt: new Date()
                    }
                },
                { new: true }
            );

            if (!updatedUser) {
                throw new Error('用户不存在');
            }

            console.log('✅ 用户软删除成功:', updatedUser.username);
            return updatedUser;
        } catch (error) {
            throw error;
        }
    }

    // 5. 批量操作
    async createMultipleUsers(usersData) {
        try {
            const cleanedUsers = usersData.map(userData =>
                this.sanitizeUserData(userData)
            );

            const users = await User.insertMany(cleanedUsers, {
                ordered: false, // 继续处理其他文档,即使某些失败
                rawResult: true // 返回详细结果
            });

            console.log(`✅ 批量创建用户成功: ${users.insertedCount}/${usersData.length}`);
            return users;
        } catch (error) {
            if (error.name === 'BulkWriteError') {
                console.log(`⚠️ 部分用户创建失败: ${error.result.insertedCount}/${usersData.length}`);
                return error.result;
            }
            throw error;
        }
    }

    async updateMultipleUsers(filter, updateData) {
        try {
            const result = await User.updateMany(filter, updateData);

            console.log(`✅ 批量更新用户: 匹配${result.matchedCount}个,更新${result.modifiedCount}个`);
            return result;
        } catch (error) {
            throw error;
        }
    }

    // 6. 高级查询
    async getUsersWithAggregation(pipeline = []) {
        try {
            const defaultPipeline = [
                {
                    $match: { isActive: true }
                },
                {
                    $addFields: {
                        fullName: {
                            $concat: ['$profile.firstName', ' ', '$profile.lastName']
                        }
                    }
                },
                {
                    $project: {
                        username: 1,
                        email: 1,
                        fullName: 1,
                        role: 1,
                        createdAt: 1,
                        lastLogin: 1
                    }
                }
            ];

            const finalPipeline = pipeline.length > 0 ? pipeline : defaultPipeline;
            const result = await User.aggregate(finalPipeline);

            return result;
        } catch (error) {
            throw error;
        }
    }

    async getUserStatistics() {
        try {
            const stats = await User.aggregate([
                {
                    $group: {
                        _id: null,
                        totalUsers: { $sum: 1 },
                        activeUsers: {
                            $sum: { $cond: [{ $eq: ['$isActive', true] }, 1, 0] }
                        },
                        verifiedUsers: {
                            $sum: { $cond: [{ $eq: ['$emailVerified', true] }, 1, 0] }
                        },
                        adminUsers: {
                            $sum: { $cond: [{ $eq: ['$role', 'admin'] }, 1, 0] }
                        }
                    }
                },
                {
                    $project: {
                        _id: 0,
                        totalUsers: 1,
                        activeUsers: 1,
                        inactiveUsers: { $subtract: ['$totalUsers', '$activeUsers'] },
                        verifiedUsers: 1,
                        unverifiedUsers: { $subtract: ['$totalUsers', '$verifiedUsers'] },
                        adminUsers: 1,
                        regularUsers: { $subtract: ['$totalUsers', '$adminUsers'] }
                    }
                }
            ]);

            return stats[0] || {};
        } catch (error) {
            throw error;
        }
    }

    // 工具方法
    sanitizeUserData(data, isUpdate = false) {
        const allowedFields = [
            'username', 'email', 'password', 'profile', 'role',
            'isActive', 'emailVerified'
        ];

        const sanitized = {};

        allowedFields.forEach(field => {
            if (data[field] !== undefined) {
                if (field === 'email' && data[field]) {
                    sanitized[field] = data[field].toLowerCase().trim();
                } else if (typeof data[field] === 'string') {
                    sanitized[field] = data[field].trim();
                } else {
                    sanitized[field] = data[field];
                }
            }
        });

        // 更新操作时不允许修改某些字段
        if (isUpdate) {
            delete sanitized.password; // 密码单独更新
            delete sanitized.emailVerificationToken;
        }

        return sanitized;
    }
}

module.exports = UserService;

console.log('MongoDB CRUD操作服务完成');

CRUD操作要点

  • 🎯 数据验证:在操作前进行完整的数据验证和清理
  • 🎯 错误处理:区分不同类型的错误并提供有意义的错误信息
  • 🎯 性能优化:使用索引、分页和适当的查询优化
  • 🎯 批量操作:支持批量创建、更新和删除操作
  • 🎯 软删除:提供软删除选项,保留数据完整性

高级查询和聚合操作

复杂查询和聚合管道

javascript
// 💎 MongoDB高级查询详解
const mongoose = require('mongoose');
const User = require('./models/User');

class AdvancedQueryService {
    // 1. 复杂条件查询
    async complexUserSearch(searchCriteria) {
        try {
            const {
                keyword,
                role,
                isActive,
                ageRange,
                dateRange,
                sortBy = 'createdAt',
                sortOrder = 'desc',
                page = 1,
                limit = 10
            } = searchCriteria;

            // 构建查询条件
            let query = {};

            // 关键词搜索
            if (keyword) {
                query.$or = [
                    { username: { $regex: keyword, $options: 'i' } },
                    { email: { $regex: keyword, $options: 'i' } },
                    { 'profile.firstName': { $regex: keyword, $options: 'i' } },
                    { 'profile.lastName': { $regex: keyword, $options: 'i' } }
                ];
            }

            // 角色筛选
            if (role && role !== 'all') {
                query.role = role;
            }

            // 活跃状态筛选
            if (isActive !== undefined) {
                query.isActive = isActive;
            }

            // 年龄范围筛选
            if (ageRange && ageRange.min !== undefined && ageRange.max !== undefined) {
                const maxDate = new Date();
                maxDate.setFullYear(maxDate.getFullYear() - ageRange.min);

                const minDate = new Date();
                minDate.setFullYear(minDate.getFullYear() - ageRange.max - 1);

                query['profile.dateOfBirth'] = {
                    $gte: minDate,
                    $lte: maxDate
                };
            }

            // 日期范围筛选
            if (dateRange && dateRange.start && dateRange.end) {
                query.createdAt = {
                    $gte: new Date(dateRange.start),
                    $lte: new Date(dateRange.end)
                };
            }

            // 排序配置
            const sortConfig = {};
            sortConfig[sortBy] = sortOrder === 'desc' ? -1 : 1;

            // 分页配置
            const skip = (page - 1) * limit;

            // 执行查询
            const [users, total] = await Promise.all([
                User.find(query)
                    .sort(sortConfig)
                    .skip(skip)
                    .limit(limit)
                    .populate('profile')
                    .lean(), // 返回普通JavaScript对象,提高性能
                User.countDocuments(query)
            ]);

            return {
                users,
                pagination: {
                    page,
                    limit,
                    total,
                    pages: Math.ceil(total / limit)
                },
                searchCriteria
            };

        } catch (error) {
            throw error;
        }
    }

    // 2. 聚合管道查询
    async getUserAnalytics() {
        try {
            const analytics = await User.aggregate([
                // 阶段1:匹配活跃用户
                {
                    $match: { isActive: true }
                },
                // 阶段2:添加计算字段
                {
                    $addFields: {
                        age: {
                            $floor: {
                                $divide: [
                                    { $subtract: [new Date(), '$profile.dateOfBirth'] },
                                    365.25 * 24 * 60 * 60 * 1000
                                ]
                            }
                        },
                        daysSinceRegistration: {
                            $floor: {
                                $divide: [
                                    { $subtract: [new Date(), '$createdAt'] },
                                    24 * 60 * 60 * 1000
                                ]
                            }
                        }
                    }
                },
                // 阶段3:按角色和年龄组分组
                {
                    $group: {
                        _id: {
                            role: '$role',
                            ageGroup: {
                                $switch: {
                                    branches: [
                                        { case: { $lt: ['$age', 18] }, then: 'under18' },
                                        { case: { $lt: ['$age', 25] }, then: '18-24' },
                                        { case: { $lt: ['$age', 35] }, then: '25-34' },
                                        { case: { $lt: ['$age', 50] }, then: '35-49' },
                                        { case: { $gte: ['$age', 50] }, then: '50+' }
                                    ],
                                    default: 'unknown'
                                }
                            }
                        },
                        count: { $sum: 1 },
                        avgAge: { $avg: '$age' },
                        avgDaysSinceRegistration: { $avg: '$daysSinceRegistration' },
                        emailVerifiedCount: {
                            $sum: { $cond: [{ $eq: ['$emailVerified', true] }, 1, 0] }
                        }
                    }
                },
                // 阶段4:重新整理数据结构
                {
                    $project: {
                        _id: 0,
                        role: '$_id.role',
                        ageGroup: '$_id.ageGroup',
                        count: 1,
                        avgAge: { $round: ['$avgAge', 1] },
                        avgDaysSinceRegistration: { $round: ['$avgDaysSinceRegistration', 0] },
                        emailVerificationRate: {
                            $round: [
                                { $multiply: [{ $divide: ['$emailVerifiedCount', '$count'] }, 100] },
                                1
                            ]
                        }
                    }
                },
                // 阶段5:排序
                {
                    $sort: { role: 1, ageGroup: 1 }
                }
            ]);

            return analytics;
        } catch (error) {
            throw error;
        }
    }

    // 3. 地理位置查询(如果有地理数据)
    async findUsersNearLocation(longitude, latitude, maxDistance = 10000) {
        try {
            const users = await User.find({
                'profile.location': {
                    $near: {
                        $geometry: {
                            type: 'Point',
                            coordinates: [longitude, latitude]
                        },
                        $maxDistance: maxDistance // 米
                    }
                }
            }).limit(50);

            return users;
        } catch (error) {
            throw error;
        }
    }

    // 4. 文本搜索
    async fullTextSearch(searchText) {
        try {
            // 需要先创建文本索引
            // db.users.createIndex({
            //     username: "text",
            //     "profile.firstName": "text",
            //     "profile.lastName": "text",
            //     "profile.bio": "text"
            // })

            const users = await User.find(
                { $text: { $search: searchText } },
                { score: { $meta: 'textScore' } }
            )
            .sort({ score: { $meta: 'textScore' } })
            .limit(20);

            return users;
        } catch (error) {
            throw error;
        }
    }

    // 5. 时间序列分析
    async getUserRegistrationTrends(days = 30) {
        try {
            const startDate = new Date();
            startDate.setDate(startDate.getDate() - days);

            const trends = await User.aggregate([
                {
                    $match: {
                        createdAt: { $gte: startDate }
                    }
                },
                {
                    $group: {
                        _id: {
                            year: { $year: '$createdAt' },
                            month: { $month: '$createdAt' },
                            day: { $dayOfMonth: '$createdAt' }
                        },
                        count: { $sum: 1 },
                        roles: { $push: '$role' }
                    }
                },
                {
                    $addFields: {
                        date: {
                            $dateFromParts: {
                                year: '$_id.year',
                                month: '$_id.month',
                                day: '$_id.day'
                            }
                        },
                        adminCount: {
                            $size: {
                                $filter: {
                                    input: '$roles',
                                    cond: { $eq: ['$$this', 'admin'] }
                                }
                            }
                        },
                        userCount: {
                            $size: {
                                $filter: {
                                    input: '$roles',
                                    cond: { $eq: ['$$this', 'user'] }
                                }
                            }
                        }
                    }
                },
                {
                    $project: {
                        _id: 0,
                        date: 1,
                        totalRegistrations: '$count',
                        adminRegistrations: '$adminCount',
                        userRegistrations: '$userCount'
                    }
                },
                {
                    $sort: { date: 1 }
                }
            ]);

            return trends;
        } catch (error) {
            throw error;
        }
    }

    // 6. 查询优化示例
    async optimizedUserQuery(filters) {
        try {
            // 使用explain()分析查询性能
            const query = User.find(filters);

            // 在开发环境中显示查询计划
            if (process.env.NODE_ENV === 'development') {
                const explanation = await query.explain('executionStats');
                console.log('查询统计:', {
                    executionTimeMillis: explanation.executionStats.executionTimeMillis,
                    totalDocsExamined: explanation.executionStats.totalDocsExamined,
                    totalDocsReturned: explanation.executionStats.totalDocsReturned,
                    indexesUsed: explanation.executionStats.executionStages
                });
            }

            // 执行优化的查询
            const users = await User.find(filters)
                .select('username email profile.firstName profile.lastName role createdAt') // 只选择需要的字段
                .lean() // 返回普通对象,提高性能
                .hint({ email: 1 }); // 强制使用特定索引

            return users;
        } catch (error) {
            throw error;
        }
    }
}

module.exports = AdvancedQueryService;

console.log('MongoDB高级查询服务完成');

高级查询要点

  • 🎯 聚合管道:使用$match、$group、$project等阶段处理复杂数据
  • 🎯 索引优化:创建合适的索引提高查询性能
  • 🎯 查询分析:使用explain()分析查询性能
  • 🎯 内存优化:使用lean()和字段选择减少内存使用
  • 🎯 地理查询:支持基于位置的查询功能

📚 MongoDB集成学习总结与下一步规划

✅ 本节核心收获回顾

通过本节Node.js MongoDB集成教程的学习,你已经掌握:

  1. MongoDB基础概念:理解了NoSQL数据库的特点和MongoDB的核心优势
  2. 数据库连接配置:掌握了多种连接方式和生产环境的最佳配置
  3. Mongoose ODM使用:学会了Schema定义、中间件、虚拟字段等高级特性
  4. 数据模型设计:掌握了文档结构设计和数据验证的最佳实践
  5. CRUD操作详解:实现了完整的数据操作和批量处理功能
  6. 高级查询技巧:学会了聚合管道、复杂查询和性能优化

🎯 MongoDB集成下一步

  1. 数据关系设计:学习MongoDB中的引用和嵌入式文档设计
  2. 事务处理:掌握MongoDB多文档事务的使用
  3. 性能监控:学习数据库性能监控和调优技巧
  4. 分片和复制:了解MongoDB的水平扩展和高可用配置

🔗 相关学习资源

💪 实践练习建议

  1. 构建博客系统:实现文章、评论、标签的完整数据模型
  2. 开发电商平台:设计产品、订单、用户的复杂关系
  3. 实现社交网络:构建用户关系、动态、消息系统
  4. 性能测试:使用大量数据测试查询性能和优化

🔍 常见问题FAQ

Q1: MongoDB和关系型数据库应该如何选择?

A: 选择依据:1)数据结构灵活性需求(MongoDB更灵活);2)事务复杂度(关系型数据库事务更强);3)扩展性需求(MongoDB水平扩展更容易);4)团队技能和项目需求;5)数据一致性要求。

Q2: Mongoose Schema设计的最佳实践是什么?

A: 最佳实践:1)合理使用嵌入式文档和引用;2)设置适当的验证规则;3)创建必要的索引;4)使用中间件处理业务逻辑;5)定义虚拟字段计算属性;6)避免过深的嵌套结构。

Q3: 如何优化MongoDB查询性能?

A: 优化策略:1)创建合适的索引;2)使用explain()分析查询;3)限制返回字段;4)使用lean()查询;5)合理使用聚合管道;6)避免全表扫描;7)使用查询缓存。

Q4: MongoDB的数据一致性如何保证?

A: 一致性保证:1)使用事务处理关键操作;2)设计合理的数据模型;3)使用原子操作;4)实现应用层一致性检查;5)使用适当的写关注级别;6)考虑最终一致性模型。

Q5: 如何处理MongoDB的大数据量查询?

A: 大数据处理:1)使用分页查询;2)创建复合索引;3)使用聚合管道分阶段处理;4)考虑数据分片;5)使用游标遍历;6)实现查询缓存;7)优化数据模型设计。


"MongoDB为现代Web应用提供了灵活强大的数据存储解决方案。掌握了MongoDB集成技术,你就具备了构建可扩展、高性能应用的重要能力。继续学习MySQL操作,丰富你的数据库技能栈!"