Search K
Appearance
Appearance
📊 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数据库最佳实践
通过本节Node.js MongoDB集成教程,你将系统性掌握:
MongoDB是什么?这是现代Web开发中的重要问题。MongoDB是一个基于文档的NoSQL数据库,以其灵活性、可扩展性和开发效率而闻名,也是现代Web应用的理想数据存储解决方案。
💡 设计理念:MongoDB让开发者能够以更自然的方式存储和操作数据
让我们从MongoDB的连接配置开始:
// 🎉 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连接配置完成');// 🔧 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要点:
// 🚀 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操作要点:
// 💎 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高级查询服务完成');高级查询要点:
通过本节Node.js MongoDB集成教程的学习,你已经掌握:
A: 选择依据:1)数据结构灵活性需求(MongoDB更灵活);2)事务复杂度(关系型数据库事务更强);3)扩展性需求(MongoDB水平扩展更容易);4)团队技能和项目需求;5)数据一致性要求。
A: 最佳实践:1)合理使用嵌入式文档和引用;2)设置适当的验证规则;3)创建必要的索引;4)使用中间件处理业务逻辑;5)定义虚拟字段计算属性;6)避免过深的嵌套结构。
A: 优化策略:1)创建合适的索引;2)使用explain()分析查询;3)限制返回字段;4)使用lean()查询;5)合理使用聚合管道;6)避免全表扫描;7)使用查询缓存。
A: 一致性保证:1)使用事务处理关键操作;2)设计合理的数据模型;3)使用原子操作;4)实现应用层一致性检查;5)使用适当的写关注级别;6)考虑最终一致性模型。
A: 大数据处理:1)使用分页查询;2)创建复合索引;3)使用聚合管道分阶段处理;4)考虑数据分片;5)使用游标遍历;6)实现查询缓存;7)优化数据模型设计。
"MongoDB为现代Web应用提供了灵活强大的数据存储解决方案。掌握了MongoDB集成技术,你就具备了构建可扩展、高性能应用的重要能力。继续学习MySQL操作,丰富你的数据库技能栈!"