想象一下,你已经在 MongoDB 中存储了大量数据,现在需要从中快速准确地找到你需要的信息。就像在图书馆中寻找特定书籍一样,你需要知道如何使用各种"检索工具"来精确定位数据。
MongoDB 的查询语法就是这些强大的检索工具。从简单的精确匹配到复杂的条件组合,从数值比较到文本搜索,掌握查询操作符是高效数据检索的基础。
今天,我们将深入探讨 MongoDB 的核心查询操作符,通过丰富的实例和实际应用场景,让你能够灵活运用各种查询技巧,成为数据检索的高手。
目录
- 为什么查询语法如此重要?
- MongoDB 查询基础架构
- 比较操作符:精确控制数据筛选
- 逻辑操作符:复杂条件组合
- 数组操作符:处理集合数据
- 字符串操作符:文本处理利器
- 嵌套文档查询:深入数据结构
- 实际应用场景
- 常见问题与解决方案
- 总结:掌握查询基础
为什么查询语法如此重要?
查询是数据库的核心功能
在数据库的世界里,存储数据只是第一步,检索数据才是核心。MongoDB 的查询语法决定了你能够:
- 快速定位:从海量数据中精确找到目标记录
- 灵活筛选:根据复杂条件组合进行数据过滤
- 高效分析:支持统计、聚合等高级数据分析操作
- 性能优化:通过合理的查询设计提升系统性能
MongoDB vs SQL 查询对比
让我们先看看 MongoDB 和 SQL 在查询语法上的对应关系:
| 查询类型 | SQL | MongoDB | 说明 |
|---|---|---|---|
| 精确匹配 | WHERE field = 'value' | {field: 'value'} | 简化的精确匹配语法 |
| 数值比较 | WHERE age > 18 | {age: {$gt: 18}} | 使用操作符进行比较 |
| 范围查询 | WHERE id IN (1,2,3) | {id: {$in: [1,2,3]}} | 数组包含查询 |
| 逻辑组合 | WHERE age > 18 AND status = 'active' | {$and: [{age: {$gt: 18}}, {status: 'active'}]} | 逻辑操作符组合 |
| 模糊查询 | WHERE name LIKE '%张%' | {name: {$regex: /张/, $options: 'i'}} | 正则表达式查询 |
| 空值检查 | WHERE field IS NULL | {field: {$exists: false}} | 存在性检查 |
MongoDB 查询的优势:
- JSON 原生:查询条件本身就是 JSON 格式,更直观
- 嵌套支持:天然支持嵌套文档和数组查询
- 灵活扩展:操作符可以灵活组合,适应复杂需求
MongoDB 查询基础架构
查询语句的基本结构
// MongoDB Shell / Node.js
db.collection.find(
{
/* 查询条件 */
}, // filter:筛选条件
{
/* 投影选项 */
} // projection:返回字段控制
);
查询条件(Filter): 决定哪些文档会被返回
投影选项(Projection): 决定返回文档的哪些字段
基础查询示例
// MongoDB Shell / Node.js
// 1. 查询所有文档
db.users.find();
// 2. 带条件的查询
db.users.find({ age: 25 });
// 3. 带投影的查询
db.users.find(
{ age: { $gte: 18 } }, // 查询条件:年龄大于等于18
{ username: 1, email: 1, _id: 0 } // 投影:只返回用户名和邮箱
);
// 4. 限制返回数量
db.products.find({ category: "电子产品" }).limit(10);
// 5. 排序结果
db.articles.find({ status: "published" }).sort({ publishedAt: -1 });
比较操作符:精确控制数据筛选
比较操作符是 MongoDB 查询的基础,用于精确控制数据的筛选条件。
相等性查询:$eq 操作符
基础用法:
// MongoDB Shell / Node.js
// 显式使用 $eq(通常可以省略)
db.users.find({ age: { $eq: 25 } });
// 简写形式(推荐)
db.users.find({ age: 25 });
// 字符串匹配
db.users.find({ status: "active" });
// 布尔值匹配
db.users.find({ isVerified: true });
// null 值匹配
db.users.find({ phone: null });
实际应用示例:
// 1. 用户状态查询
db.users.find({ status: "active" });
// 2. 产品分类查询
db.products.find({ category: "电子产品" });
// 3. 订单状态查询
db.orders.find({ orderStatus: "completed" });
// 4. 多字段精确匹配
db.users.find({
username: "zhangsan",
status: "active",
});
数值比较:$gt, $gte, $lt, $lte
操作符说明:
$gt:大于(greater than)$gte:大于等于(greater than or equal)$lt:小于(less than)$lte:小于等于(less than or equal)
基础用法:
// MongoDB Shell / Node.js
// 1. 大于比较
db.users.find({ age: { $gt: 18 } }); // 年龄大于18
db.products.find({ price: { $gt: 100 } }); // 价格大于100
// 2. 大于等于比较
db.users.find({ age: { $gte: 18 } }); // 年龄大于等于18
db.orders.find({ total: { $gte: 1000 } }); // 订单总额大于等于1000
// 3. 小于比较
db.users.find({ age: { $lt: 65 } }); // 年龄小于65
db.products.find({ stock: { $lt: 10 } }); // 库存小于10
// 4. 小于等于比较
db.users.find({ age: { $lte: 65 } }); // 年龄小于等于65
db.products.find({ rating: { $lte: 4.5 } }); // 评分小于等于4.5
实际应用场景:
// 1. 电商价格筛选
db.products.find({
price: { $gte: 100, $lte: 1000 }, // 价格在100-1000之间
});
// 2. 用户年龄分组
db.users.find({
age: { $gte: 18, $lt: 30 }, // 18-29岁用户
});
// 3. 库存预警
db.products.find({
stock: { $lt: 10 }, // 库存小于10
status: "active", // 且状态为活跃
});
// 4. 评分筛选
db.products.find({
rating: { $gte: 4.0 }, // 评分大于等于4.0
reviewCount: { $gte: 100 }, // 且评论数大于等于100
});
// 5. 日期范围查询
db.orders.find({
createdAt: {
$gte: new Date("2024-01-01"),
$lt: new Date("2024-02-01"),
},
});
范围查询:$in 和 $nin
$in 操作符: 匹配数组中任意一个值
$nin 操作符: 不匹配数组中的任何值
// MongoDB Shell / Node.js
// 1. $in 操作符使用
db.users.find({
role: { $in: ["admin", "moderator"] },
});
db.products.find({
category: { $in: ["电子产品", "服装", "图书"] },
});
db.orders.find({
status: { $in: ["pending", "processing"] },
});
// 2. $nin 操作符使用
db.users.find({
status: { $nin: ["banned", "deleted"] },
});
db.products.find({
category: { $nin: ["已下架", "测试商品"] },
});
// 3. 数值范围查询
db.users.find({
age: { $in: [18, 25, 30, 35] },
});
db.products.find({
price: { $in: [99, 199, 299, 399] },
});
实际应用示例:
// 1. 多状态查询
db.orders.find({
status: { $in: ["pending", "confirmed", "shipped"] },
});
// 2. 排除特定用户
db.comments.find({
userId: { $nin: ["spam_user1", "spam_user2"] },
});
// 3. 特定地区用户
db.users.find({
"address.province": { $in: ["北京", "上海", "广东"] },
});
// 4. 特定价格点商品
db.products.find({
price: { $in: [99, 199, 299, 499, 999] },
status: "active",
});
存在性检查:$exists
$exists 操作符: 检查字段是否存在
// MongoDB Shell / Node.js
// 1. 字段存在检查
db.users.find({ phone: { $exists: true } }); // 有手机号的用户
db.users.find({ avatar: { $exists: true } }); // 有头像的用户
// 2. 字段不存在检查
db.users.find({ phone: { $exists: false } }); // 没有手机号的用户
db.users.find({ avatar: { $exists: false } }); // 没有头像的用户
// 3. 结合其他条件
db.users.find({
phone: { $exists: true, $ne: null }, // 有手机号且不为null
status: "active",
});
db.products.find({
description: { $exists: true, $ne: "" }, // 有描述且不为空
price: { $gte: 100 },
});
实际应用场景:
// 1. 数据完整性检查
db.users.find({
email: { $exists: false }, // 找出缺少邮箱的用户
});
// 2. 可选字段查询
db.products.find({
tags: { $exists: true }, // 有标签的产品
category: "电子产品",
});
// 3. 用户资料完整性
db.users.find({
$and: [
{ profile: { $exists: true } },
{ "profile.bio": { $exists: true } },
{ "profile.avatar": { $exists: true } },
],
});
逻辑操作符:复杂条件组合
当简单的比较操作符无法满足复杂查询需求时,逻辑操作符就能派上用场了。
AND 逻辑:$and 操作符
$and 操作符: 所有条件都必须满足
// MongoDB Shell / Node.js
// 1. 显式使用 $and
db.users.find({
$and: [{ age: { $gte: 18 } }, { status: "active" }, { role: "user" }],
});
// 2. 隐式 AND(推荐,更简洁)
db.users.find({
age: { $gte: 18 },
status: "active",
role: "user",
});
// 3. 同一字段的多个条件(必须使用 $and)
db.products.find({
$and: [{ price: { $gte: 100 } }, { price: { $lte: 1000 } }],
});
实际应用示例:
// 1. 复杂用户筛选
db.users.find({
age: { $gte: 18, $lte: 65 }, // 年龄范围
status: "active", // 状态活跃
"address.city": "北京", // 城市
createdAt: { $gte: new Date("2023-01-01") }, // 注册时间
});
// 2. 产品多条件筛选
db.products.find({
category: "电子产品",
price: { $gte: 100, $lte: 5000 },
stock: { $gt: 0 },
rating: { $gte: 4.0 },
status: "active",
});
// 3. 订单状态查询
db.orders.find({
status: "completed",
total: { $gte: 1000 },
"customer.vipLevel": { $gte: 2 },
createdAt: {
$gte: new Date("2024-01-01"),
$lt: new Date("2024-02-01"),
},
});
OR 逻辑:$or 操作符
$or 操作符: 满足任意一个条件即可
// MongoDB Shell / Node.js
// 1. 基础 OR 查询
db.users.find({
$or: [{ role: "admin" }, { role: "moderator" }],
});
// 2. 多字段 OR 查询
db.users.find({
$or: [{ username: "admin" }, { email: "admin@example.com" }],
});
// 3. 复杂 OR 条件
db.products.find({
$or: [
{ category: "电子产品", price: { $lt: 500 } },
{ category: "服装", price: { $lt: 200 } },
{ rating: { $gte: 4.5 } },
],
});
实际应用场景:
// 1. 权限查询
db.users.find({
$or: [
{ role: "admin" },
{ role: "super_admin" },
{ permissions: { $in: ["read", "write"] } },
],
});
// 2. 搜索功能
db.articles.find({
$or: [
{ title: { $regex: "MongoDB", $options: "i" } },
{ content: { $regex: "MongoDB", $options: "i" } },
{ tags: { $in: ["MongoDB", "数据库"] } },
],
});
// 3. 多状态查询
db.orders.find({
status: { $in: ["pending", "confirmed", "shipped"] },
$or: [{ priority: "high" }, { total: { $gte: 1000 } }],
});
NOT 逻辑:$not 和 $nor
$not 操作符: 不满足指定条件
$nor 操作符: 都不满足指定条件
// MongoDB Shell / Node.js
// 1. $not 操作符
db.users.find({
age: { $not: { $lt: 18 } }, // 年龄不小于18(即大于等于18)
});
db.products.find({
price: { $not: { $gte: 1000 } }, // 价格不大于等于1000(即小于1000)
});
// 2. $nor 操作符
db.users.find({
$nor: [{ status: "banned" }, { status: "deleted" }, { role: "guest" }],
});
db.products.find({
$nor: [{ stock: 0 }, { status: "discontinued" }, { price: { $lt: 10 } }],
});
实际应用示例:
// 1. 排除特定用户
db.comments.find({
$nor: [
{ userId: { $in: ["spam_user1", "spam_user2"] } },
{ content: { $regex: "广告|推广", $options: "i" } },
],
});
// 2. 有效商品查询
db.products.find({
$nor: [
{ stock: { $lte: 0 } },
{ status: { $in: ["discontinued", "out_of_stock"] } },
{ price: { $lt: 1 } },
],
});
// 3. 复杂排除逻辑
db.users.find({
status: "active",
$nor: [
{ age: { $lt: 18 } },
{ age: { $gt: 65 } },
{ "address.country": { $ne: "中国" } },
],
});
数组操作符:处理集合数据
MongoDB 中的数组是强大的数据结构,数组操作符让我们能够灵活地查询和处理数组数据。
数组元素匹配:$all 和 $elemMatch
$all 操作符: 数组包含所有指定元素
$elemMatch 操作符: 数组元素满足所有指定条件
// MongoDB Shell / Node.js
// 1. $all 操作符
db.articles.find({
tags: { $all: ["MongoDB", "数据库"] }, // 标签同时包含 MongoDB 和 数据库
});
db.users.find({
skills: { $all: ["JavaScript", "Node.js"] }, // 技能同时包含 JavaScript 和 Node.js
});
// 2. $elemMatch 操作符
db.orders.find({
items: {
$elemMatch: {
category: "电子产品",
quantity: { $gte: 2 },
},
},
});
db.users.find({
addresses: {
$elemMatch: {
city: "北京",
isDefault: true,
},
},
});
实际应用场景:
// 1. 文章标签组合查询
db.articles.find({
tags: { $all: ["技术", "教程"] },
status: "published",
});
// 2. 用户技能匹配
db.users.find({
skills: { $all: ["Python", "数据分析"] },
experience: { $gte: 3 },
});
// 3. 订单商品查询
db.orders.find({
items: {
$elemMatch: {
category: "电子产品",
price: { $gte: 1000 },
quantity: { $gte: 1 },
},
},
});
// 4. 嵌套数组查询
db.articles.find({
comments: {
$elemMatch: {
rating: { $gte: 4 },
"author.level": "expert",
},
},
});
数组大小控制:$size
$size 操作符: 匹配指定长度的数组
// MongoDB Shell / Node.js
// 1. 精确长度匹配
db.articles.find({
tags: { $size: 3 }, // 标签数组长度为3
});
db.users.find({
addresses: { $size: 1 }, // 只有一个地址
});
// 2. 空数组检查
db.products.find({
images: { $size: 0 }, // 没有图片
});
db.users.find({
orders: { $size: 0 }, // 没有订单记录
});
// 3. 实际应用场景
// 注意:$size 不支持范围查询,下面的写法是错误的
// db.articles.find({ tags: { $size: { $gte: 2 } } }) // ❌ 错误语法
// 替代方案:使用 $expr 进行范围查询
db.articles.find({
$expr: { $gte: [{ $size: "$tags" }, 2] }, // 至少2个标签
});
$size 的局限性:
// ❌ $size 不支持范围查询
db.articles.find({
tags: { $size: { $gte: 2 } }, // 错误语法
});
// ✅ 使用 $expr 实现范围查询
db.articles.find({
$expr: {
$and: [
{ $gte: [{ $size: "$tags" }, 2] },
{ $lte: [{ $size: "$tags" }, 5] },
],
},
});
字符串操作符:文本处理利器
字符串操作符让我们能够进行灵活的文本查询和匹配。
正则表达式:$regex
$regex 操作符: 使用正则表达式进行模式匹配
// MongoDB Shell / Node.js
// 1. 基础正则查询
db.users.find({
username: { $regex: /^张/, $options: "i" }, // 用户名以"张"开头,忽略大小写
});
db.articles.find({
title: { $regex: "MongoDB", $options: "i" }, // 标题包含"MongoDB"
});
// 2. 常用正则模式
db.users.find({
email: { $regex: /@gmail\.com$/, $options: "i" }, // Gmail邮箱
});
db.products.find({
name: { $regex: /iPhone|iPad/, $options: "i" }, // 包含iPhone或iPad
});
db.users.find({
phone: { $regex: /^1[3-9]\d{9}$/ }, // 中国手机号格式
});
正则表达式选项:
i:忽略大小写m:多行模式x:忽略空白字符s:单行模式
// 实际应用示例
// 1. 邮箱域名查询
db.users.find({
email: { $regex: /@(gmail|qq|163)\.com$/, $options: "i" },
});
// 2. 用户名格式验证
db.users.find({
username: { $regex: /^[a-zA-Z0-9_]{3,20}$/ }, // 3-20位字母数字下划线
});
// 3. 内容关键词搜索
db.articles.find({
$or: [
{ title: { $regex: "MongoDB|数据库", $options: "i" } },
{ content: { $regex: "MongoDB|数据库", $options: "i" } },
],
});
// 4. 价格格式查询
db.products.find({
priceDisplay: { $regex: /^\$?\d+\.\d{2}$/ }, // 价格格式如 $99.99
});
文本搜索:$text
$text 操作符: 全文搜索(需要创建文本索引)
// MongoDB Shell / Node.js
// 1. 创建文本索引
db.articles.createIndex({
title: "text",
content: "text",
tags: "text",
});
// 2. 基础文本搜索
db.articles.find({
$text: { $search: "MongoDB 数据库" },
});
// 3. 排除特定词汇
db.articles.find({
$text: {
$search: "MongoDB -教程", // 搜索包含MongoDB但不包含教程的文章
$caseSensitive: false,
},
});
// 4. 短语搜索
db.articles.find({
$text: {
$search: '"MongoDB 教程"', // 精确短语搜索
$caseSensitive: false,
},
});
// 5. 文本搜索评分
db.articles
.find({ $text: { $search: "数据库优化" } }, { score: { $meta: "textScore" } })
.sort({ score: { $meta: "textScore" } });
文本索引配置:
// 1. 权重配置
db.articles.createIndex(
{
title: "text",
content: "text",
},
{
weights: {
title: 10, // 标题权重更高
content: 1,
},
}
);
// 2. 语言配置
db.articles.createIndex(
{
title: "text",
content: "text",
},
{
default_language: "chinese", // 中文分词
}
);
// 3. 实际应用
db.articles
.find({
$text: { $search: "性能优化" },
status: "published",
})
.sort({ score: { $meta: "textScore" } });
嵌套文档查询:深入数据结构
MongoDB 支持嵌套文档,查询时需要使用点号表示法来访问嵌套字段。
点号表示法查询
// MongoDB Shell / Node.js
// 1. 单层嵌套查询
db.users.find({
"address.city": "北京",
});
db.products.find({
"specifications.color": "黑色",
});
// 2. 多层嵌套查询
db.users.find({
"profile.social.wechat": { $exists: true },
});
db.orders.find({
"customer.address.province": "广东",
});
// 3. 数组中的嵌套文档
db.users.find({
"addresses.city": "上海", // addresses是数组,查询数组中元素的city字段
});
db.products.find({
"reviews.rating": { $gte: 4 }, // 查询评分大于等于4的评论
});
嵌套文档的复杂查询
// 1. 嵌套文档的多个条件
db.users.find({
"address.city": "北京",
"address.district": "朝阳区",
"address.isDefault": true,
});
// 2. 结合数组操作符
db.orders.find({
"items.product.category": "电子产品",
"items.quantity": { $gte: 2 },
});
// 3. 嵌套文档的存在性检查
db.users.find({
"profile.avatar": { $exists: true },
"profile.bio": { $exists: true },
});
// 4. 实际应用场景
db.articles.find({
"author.profile.level": "expert",
tags: { $in: ["MongoDB", "数据库"] },
"metadata.views": { $gte: 1000 },
});
实际应用场景
掌握了各种查询操作符后,让我们看看如何在实际业务场景中应用它们。
场景 1:电商商品筛选系统
// Node.js 示例 - 商品筛选API
async function searchProducts(filters) {
const query = {};
// 价格范围筛选
if (filters.minPrice || filters.maxPrice) {
query.price = {};
if (filters.minPrice) query.price.$gte = filters.minPrice;
if (filters.maxPrice) query.price.$lte = filters.maxPrice;
}
// 分类筛选
if (filters.categories && filters.categories.length > 0) {
query.category = { $in: filters.categories };
}
// 品牌筛选
if (filters.brands && filters.brands.length > 0) {
query.brand = { $in: filters.brands };
}
// 评分筛选
if (filters.minRating) {
query.rating = { $gte: filters.minRating };
}
// 库存筛选
if (filters.inStock) {
query.stock = { $gt: 0 };
}
// 状态筛选
query.status = "active";
// 关键词搜索
if (filters.keyword) {
query.$or = [
{ name: { $regex: filters.keyword, $options: "i" } },
{ description: { $regex: filters.keyword, $options: "i" } },
{ tags: { $in: [filters.keyword] } },
];
}
const products = await db.products
.find(query)
.sort({
[filters.sortBy || "createdAt"]: filters.sortOrder === "asc" ? 1 : -1,
})
.limit(filters.limit || 20)
.skip((filters.page - 1) * (filters.limit || 20))
.toArray();
return products;
}
场景 2:用户管理系统
// Node.js 示例 - 用户查询系统
async function searchUsers(searchParams) {
const query = {};
// 基础筛选
if (searchParams.status) {
query.status = searchParams.status;
}
if (searchParams.role) {
query.role = { $in: searchParams.role };
}
// 年龄范围
if (searchParams.ageRange) {
query.age = {};
if (searchParams.ageRange.min) query.age.$gte = searchParams.ageRange.min;
if (searchParams.ageRange.max) query.age.$lte = searchParams.ageRange.max;
}
// 地理位置筛选
if (searchParams.city) {
query["address.city"] = searchParams.city;
}
if (searchParams.province) {
query["address.province"] = searchParams.province;
}
// 注册时间范围
if (searchParams.registeredAfter) {
query.createdAt = { $gte: new Date(searchParams.registeredAfter) };
}
// 用户资料完整性
if (searchParams.hasProfile) {
query.$and = [
{ "profile.name": { $exists: true } },
{ "profile.avatar": { $exists: true } },
];
}
// 搜索关键词
if (searchParams.keyword) {
query.$or = [
{ username: { $regex: searchParams.keyword, $options: "i" } },
{ email: { $regex: searchParams.keyword, $options: "i" } },
{ "profile.name": { $regex: searchParams.keyword, $options: "i" } },
];
}
const users = await db.users
.find(query)
.sort({ createdAt: -1 })
.limit(searchParams.limit || 50)
.toArray();
return users;
}
场景 3:内容管理系统
// Node.js 示例 - 文章内容查询
async function searchArticles(searchOptions) {
const query = {};
// 状态筛选
query.status = "published";
// 分类筛选
if (searchOptions.category) {
query.category = searchOptions.category;
}
// 标签筛选
if (searchOptions.tags && searchOptions.tags.length > 0) {
query.tags = { $in: searchOptions.tags };
}
// 作者筛选
if (searchOptions.author) {
query["author.username"] = searchOptions.author;
}
// 发布时间范围
if (searchOptions.publishedAfter) {
query.publishedAt = { $gte: new Date(searchOptions.publishedAfter) };
}
// 热门文章筛选
if (searchOptions.isPopular) {
query.$or = [
{ "stats.views": { $gte: 1000 } },
{ "stats.likes": { $gte: 100 } },
];
}
// 全文搜索
if (searchOptions.searchText) {
query.$text = { $search: searchOptions.searchText };
}
// 构建排序
let sortOptions = {};
if (searchOptions.sortBy) {
switch (searchOptions.sortBy) {
case "popular":
sortOptions = { "stats.views": -1 };
break;
case "recent":
sortOptions = { publishedAt: -1 };
break;
case "trending":
sortOptions = { "stats.likes": -1 };
break;
default:
sortOptions = { publishedAt: -1 };
}
}
const articles = await db.articles
.find(query)
.sort(sortOptions)
.limit(searchOptions.limit || 20)
.toArray();
return articles;
}
常见问题与解决方案
问题 1:查询性能慢
原因分析:
- 缺少合适的索引
- 查询条件不够精确
- 返回数据量过大
解决方案:
// 1. 创建合适的索引
db.users.createIndex({ email: 1 });
db.products.createIndex({ category: 1, price: -1 });
db.articles.createIndex({ status: 1, publishedAt: -1 });
// 2. 使用投影减少数据传输
db.users.find(
{ status: "active" },
{ username: 1, email: 1, _id: 0 } // 只返回需要的字段
);
// 3. 使用 limit 限制结果数量
db.products.find({ category: "电子产品" }).limit(100);
// 4. 使用 explain 分析查询性能
db.users.find({ email: "test@example.com" }).explain("executionStats");
问题 2:正则表达式性能问题
问题: 正则表达式查询可能导致全表扫描
解决方案:
// ❌ 不好的做法:前缀正则查询
db.users.find({ username: { $regex: /^张/ } });
// ✅ 好的做法:使用文本索引
db.users.createIndex({ username: "text" });
db.users.find({ $text: { $search: "张" } });
// ✅ 或者使用前缀索引
db.users.createIndex({ username: 1 });
db.users.find({ username: { $regex: /^张/ } }); // 现在可以使用索引
问题 3:数组查询的陷阱
问题: 数组查询可能返回意外结果
// 数据示例
{
_id: 1,
tags: ["MongoDB", "数据库", "NoSQL"],
categories: ["技术", "教程"]
}
// 查询:想要同时包含 "MongoDB" 和 "教程"
// ❌ 错误的理解:这样会匹配同时满足两个条件的文档
db.articles.find({
tags: "MongoDB",
categories: "教程"
});
// ✅ 正确的做法:如果想要在同一个数组中同时包含,使用 $all
db.articles.find({
tags: { $all: ["MongoDB", "教程"] }
});
// 或者,如果想要在同一个数组中同时包含多个元素
db.articles.find({
tags: { $all: ["MongoDB", "教程"] }
});
问题 4:null 值查询的困惑
// 数据示例
{ name: "张三", phone: null }
{ name: "李四" } // 没有 phone 字段
// 查询包含 null 值的文档
db.users.find({ phone: null }); // 会匹配两种情况
// 只查询 phone 字段存在且为 null 的文档
db.users.find({ phone: { $eq: null } });
// 查询 phone 字段不存在的文档
db.users.find({ phone: { $exists: false } });
// 查询 phone 字段存在且不为 null 的文档
db.users.find({
phone: { $exists: true, $ne: null }
});
总结:掌握查询基础
通过本文的学习,我们已经掌握了 MongoDB 查询语法的核心操作符:
核心操作符回顾
- 比较操作符:
$eq,$gt,$gte,$lt,$lte,$in,$nin,$exists - 逻辑操作符:
$and,$or,$not,$nor - 数组操作符:
$all,$elemMatch,$size - 字符串操作符:
$regex,$text - 嵌套查询:点号表示法访问嵌套字段
最佳实践要点
- 索引优化:为常用查询字段创建合适的索引
- 查询精确性:尽量使用精确的查询条件,避免全表扫描
- 投影使用:只返回需要的字段,减少网络传输
- 结果限制:使用
limit()控制返回数据量 - 性能监控:使用
explain()分析查询性能
适用场景建议
- 精确匹配:使用相等性查询和
$in操作符 - 范围查询:使用比较操作符组合
- 复杂条件:使用逻辑操作符组合多个条件
- 数组处理:使用
$all,$elemMatch处理数组数据 - 文本搜索:使用
$regex或$text进行文本匹配 - 嵌套查询:使用点号表示法访问嵌套字段
下一步学习路径
掌握了查询语法基础后,建议继续学习:
- 高级查询技巧:分页查询、游标查询、聚合查询
- 查询性能优化:索引策略、执行计划分析
- 复杂业务场景:多表关联、数据统计、报表生成
企业级开发注意事项
查询性能优化策略
在企业环境中,查询性能直接影响用户体验和系统稳定性:
1. 索引策略管理
// 为常用查询字段创建索引
db.users.createIndex({ email: 1 }); // 单字段索引
db.products.createIndex({ category: 1, price: 1 }); // 复合索引
db.articles.createIndex({ title: "text", content: "text" }); // 文本索引
// 分析查询性能
db.users.find({ email: "user@example.com" }).explain("executionStats");
2. 查询优化原则
- 避免全表扫描:确保查询条件能够使用索引
- 合理使用投影:只返回必要的字段
- 控制结果集大小:使用
limit()和skip()进行分页 - 避免正则表达式开头通配符:
/^pattern/比/pattern/更高效
3. 内存管理考虑
// 大结果集处理
const cursor = db.largeCollection.find({ status: "active" });
cursor.batchSize(1000); // 控制批处理大小
// 游标超时设置
const cursor = db.collection.find(query).maxTimeMS(30000); // 30秒超时
数据一致性保障
1. 查询一致性级别
// 设置读取关注级别
db.collection.find(query).readConcern("majority");
// 设置读取偏好
db.collection.find(query).readPreference("primary");
2. 事务中的查询
// 在事务中执行查询
const session = db.getMongo().startSession();
session.startTransaction();
try {
const user = db.users.findOne({ _id: userId }, { session });
const orders = db.orders.find({ userId: userId }, { session });
session.commitTransaction();
} catch (error) {
session.abortTransaction();
throw error;
}
安全查询实践
1. 防止注入攻击
// ❌ 危险:直接拼接用户输入
const query = { name: userInput }; // 可能被恶意利用
// ✅ 安全:使用参数化查询
const query = { name: { $regex: userInput, $options: "i" } };
// ✅ 更安全:使用白名单验证
const allowedFields = ["name", "email", "age"];
const query = {};
allowedFields.forEach((field) => {
if (filters[field]) {
query[field] = filters[field];
}
});
2. 权限控制
// 基于用户角色的查询限制
function buildUserQuery(userRole, baseQuery) {
const roleQueries = {
admin: baseQuery,
manager: { ...baseQuery, department: userRole.department },
user: { ...baseQuery, createdBy: userRole.userId },
};
return roleQueries[userRole.level] || {};
}
监控和调试
1. 查询性能监控
// 启用慢查询日志
db.setProfilingLevel(2, { slowms: 100 });
// 分析查询性能
db.system.profile.find().sort({ ts: -1 }).limit(5);
// 使用 explain 分析执行计划
db.users.find({ email: "test@example.com" }).explain("executionStats");
2. 查询调试技巧
// 使用 explain 分析查询
const explainResult = db.users.find(query).explain("executionStats");
console.log("执行时间:", explainResult.executionStats.executionTimeMillis);
console.log("扫描文档数:", explainResult.executionStats.totalDocsExamined);
console.log("返回文档数:", explainResult.executionStats.totalDocsReturned);
// 检查索引使用情况
if (
explainResult.executionStats.totalDocsExamined >
explainResult.executionStats.totalDocsReturned * 10
) {
console.warn("查询可能效率低下,建议优化索引");
}
企业级最佳实践
1. 查询设计原则
- 单一职责:每个查询只做一件事
- 可预测性:查询结果应该是可预测的
- 可维护性:查询逻辑应该清晰易懂
- 可扩展性:考虑数据量增长对查询性能的影响
2. 团队协作规范
// 查询模板和规范
const QueryTemplates = {
// 用户查询模板
findActiveUsers: (filters) => ({
status: "active",
...filters,
createdAt: { $gte: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000) },
}),
// 分页查询模板
paginatedQuery: (query, page = 1, limit = 20) => ({
...query,
$skip: (page - 1) * limit,
$limit: limit,
}),
};
3. 生产环境注意事项
- 查询超时设置:避免长时间运行的查询影响系统性能
- 资源限制:合理设置查询的内存和 CPU 使用限制
- 监控告警:设置查询性能监控和异常告警
- 备份策略:确保查询操作不会影响数据备份
下一篇文章,我们将深入探讨 MongoDB 的高级查询技巧,包括分页查询、游标查询、查询性能优化等内容。敬请期待!
3337

被折叠的 条评论
为什么被折叠?



