



















在构建现代 Web 应用时,我们经常需要在关系型数据库(MySQL、PostgreSQL)和文档型数据库(MongoDB)之间做选择。MongoDB 以其灵活的文档模型、强大的查询能力和水平扩展特性,成为许多开发者的首选。本篇文章将带你从零开始掌握 MongoDB 的核心用法。
| 概念 | 关系型数据库 | MongoDB |
|---|---|---|
| 数据库 | Database | Database |
| 表 | Table | Collection(集合) |
| 行 | Row | Document(文档) |
| 列 | Column | Field(字段) |
| 主键 | Primary Key | _id(自动生成) |
macOS(使用 Homebrew):
brew tap mongodb/brew
brew install mongodb-community
brew services start mongodb-communityDocker 方式(推荐):
docker run -d -p 27017:27017 --name mongodb mongo:latestmongodb://[username:password@]host[:port][/database][?options]示例:
mongodb://localhost:27017/myapp
mongodb://user:pass@localhost:27017/myapp?authSource=admin
mongodb+srv://user:pass@cluster.mongodb.net/myapp27017mongodb+srv:// 协议使用 mongoose 库(最流行的 MongoDB ODM):
import mongoose from 'mongoose';
// 连接 MongoDB
const connectDB = async () => {
try {
await mongoose.connect('mongodb://localhost:27017/myapp', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log('MongoDB 连接成功');
} catch (error) {
console.error('连接失败:', error);
process.exit(1);
}
};
connectDB();
// 定义 Schema(数据模型)
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, unique: true, required: true },
age: { type: Number, min: 0 },
tags: [String],
createdAt: { type: Date, default: Date.now },
});
// 创建 Model
const User = mongoose.model('User', userSchema);mongoose.connect() 返回 Promise,支持 async/await// 单条插入
const newUser = await User.create({
name: '张三',
email: 'zhangsan@example.com',
age: 25,
tags: ['前端', 'Node.js'],
});
// 或使用 save()
const user = new User({ name: '李四', email: 'lisi@example.com' });
await user.save();
// 批量插入
await User.insertMany([
{ name: '王五', email: 'wangwu@example.com' },
{ name: '赵六', email: 'zhaoliu@example.com' },
]);// 查询所有
const users = await User.find();
// 条件查询
const user = await User.findOne({ email: 'zhangsan@example.com' });
const adults = await User.find({ age: { $gte: 18 } });
// 投影(只返回指定字段)
const names = await User.find({}, 'name email');
// 排序与分页
const users = await User.find()
.sort({ createdAt: -1 })
.skip(10)
.limit(20);
// 复杂查询
const result = await User.find({
age: { $gte: 18, $lte: 65 },
tags: { $in: ['前端', '后端'] },
name: { $regex: /张/, $options: 'i' },
});常用查询操作符:
$gt、$gte、$lt、$lte:比较$in、$nin:包含/不包含$regex:正则匹配$exists:字段存在性$or、$and:逻辑运算// 更新单条(返回更新后的文档)
const updated = await User.findOneAndUpdate(
{ email: 'zhangsan@example.com' },
{ age: 26 },
{ new: true } // 返回更新后的文档
);
// 更新多条
await User.updateMany(
{ age: { $lt: 18 } },
{ $set: { status: 'minor' } }
);
// 使用操作符
await User.updateOne(
{ _id: userId },
{
$inc: { age: 1 }, // 自增
$push: { tags: '新标签' }, // 数组追加
$set: { updatedAt: Date.now() }
}
);常用更新操作符:
$set:设置字段值$unset:删除字段$inc:数值增减$push、$pull:数组操作$addToSet:数组去重追加// 删除单条
await User.deleteOne({ email: 'zhangsan@example.com' });
// 删除多条
await User.deleteMany({ age: { $lt: 18 } });
// 查找并删除(返回被删除的文档)
const deleted = await User.findOneAndDelete({ _id: userId });聚合管道是 MongoDB 最强大的功能之一,类似 SQL 的 GROUP BY:
// 统计各年龄段用户数量
const stats = await User.aggregate([
{ $match: { age: { $exists: true } } }, // 过滤
{ $group: {
_id: { $switch: {
branches: [
{ case: { $lt: ['$age', 18] }, then: '未成年' },
{ case: { $lt: ['$age', 30] }, then: '青年' },
{ case: { $lt: ['$age', 50] }, then: '中年' },
{ case: true, then: '老年' }
],
default: '未知'
}},
count: { $sum: 1 },
avgAge: { $avg: '$age' }
}},
{ $sort: { count: -1 } }
]);
// 查找包含特定标签的用户,并展开标签数组
const result = await User.aggregate([
{ $match: { tags: { $exists: true, $ne: [] } } },
{ $unwind: '$tags' },
{ $group: {
_id: '$tags',
users: { $push: '$name' }
}}
]);常用聚合阶段:
$match:过滤文档$group:分组统计$project:字段投影$sort:排序$limit、$skip:分页$unwind:展开数组$lookup:关联查询(类似 JOIN)索引能大幅提升查询性能:
// 创建单字段索引
await User.createIndex({ email: 1 }); // 1 升序,-1 降序
// 创建复合索引
await User.createIndex({ name: 1, age: -1 });
// 创建文本索引(支持全文搜索)
await User.createIndex({ name: 'text', bio: 'text' });
// 查看索引
const indexes = await User.collection.getIndexes();
// 删除索引
await User.collection.dropIndex('email_1');索引策略建议:
explain() 分析查询计划// 分析查询性能
const explain = await User.find({ email: 'test@example.com' }).explain();
console.log(explain.executionStats);结合《博客搭建二-文章接口》,用 MongoDB 实现文章管理:
import mongoose from 'mongoose';
// 文章 Schema
const articleSchema = new mongoose.Schema({
title: { type: String, required: true, index: true },
content: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
tags: [String],
views: { type: Number, default: 0 },
published: { type: Boolean, default: false },
publishedAt: Date,
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
});
// 自动更新 updatedAt
articleSchema.pre('save', function(next) {
this.updatedAt = Date.now();
next();
});
const Article = mongoose.model('Article', articleSchema);
// 创建文章
const createArticle = async (data) => {
return await Article.create({
...data,
publishedAt: data.published ? Date.now() : null,
});
};
// 查询已发布文章(带作者信息)
const getPublishedArticles = async (page = 1, limit = 10) => {
return await Article.find({ published: true })
.populate('author', 'name email') // 关联查询用户信息
.sort({ publishedAt: -1 })
.skip((page - 1) * limit)
.limit(limit);
};
// 增加浏览量
const incrementViews = async (articleId) => {
return await Article.findByIdAndUpdate(
articleId,
{ $inc: { views: 1 } },
{ new: true }
);
};
// 按标签统计文章数
const getTagStats = async () => {
return await Article.aggregate([
{ $match: { published: true } },
{ $unwind: '$tags' },
{ $group: {
_id: '$tags',
count: { $sum: 1 },
articles: { $push: { title: '$title', id: '$_id' } }
}},
{ $sort: { count: -1 } }
]);
};连接池管理
mongoose.connect(uri, {
maxPoolSize: 10, // 最大连接数
minPoolSize: 5, // 最小连接数
serverSelectionTimeoutMS: 5000,
});批量操作优化
// 使用 bulkWrite 替代多次 updateOne
await User.bulkWrite([
{ updateOne: { filter: { _id: id1 }, update: { $set: { status: 'active' } } } },
{ updateOne: { filter: { _id: id2 }, update: { $set: { status: 'inactive' } } } },
]);查询优化
select() 只查询需要的字段$regex 在大量数据上的全表扫描lean() 返回纯 JavaScript 对象(不创建 Mongoose 文档实例)const users = await User.find().lean(); // 性能更好,但失去 Mongoose 方法const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
validate: {
validator: (v) => /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(v),
message: '邮箱格式不正确'
}
}
});MongoServerError: E11000 duplicate key error
MongooseError: Operationusers.findOne()buffering timed out after 10000ms
查询性能慢
explain() 分析查询计划,检查是否使用了索引。CastError: Cast to ObjectId failed
_id 是有效的 MongoDB ObjectId 格式(24 位十六进制字符串)。内存占用过高
lean() 查询,避免创建大量 Mongoose 文档实例。mongoose 可以简化操作,但要注意连接池和查询优化。掌握 MongoDB 的核心用法和优化技巧,你就能在项目中灵活选择数据存储方案,构建高性能、可扩展的 Web 应用。
原文地址:https://webfem.com/post/mongodb-usage-guide,转载请注明出处
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。