01. 比较与逻辑操作符大全
MongoDB 查询条件用操作符来描述,都是以美元符号开头的。比较操作符:美元 eq(等于,一般不需要显式写)、美元 ne(不等于)、美元 gt(大于)、美元 gte(大于等于)、美元 lt(小于)、美元 lte(小于等于)、美元 in(在列表中)、美元 nin(不在列表中)。
逻辑操作符:美元 and(且)、美元 or(或)、美元 nor(都不)、美元 not(非)。注意美元 and 通常不需要显式写——逗号分隔的多个条件默认就是 AND。
数组操作符:美元 all(数组包含所有指定元素)、美元 size(数组长度匹配)、美元 elemMatch(数组中至少一个元素满足条件)。
正则表达式:字段值可以直接传正则来做模糊匹配。MongoDB 用的是 PCRE 正则语法。
javascript
// 比较操作符
db.products.find({ price: { $gte: 100, $lte: 500 } });
db.users.find({ city: { $in: ["北京", "上海", "深圳"] } });
// 逻辑操作符
db.users.find({
$and: [{ age: { $gte: 18 } }, { age: { $lte: 60 } }]
});
db.users.find({
$or: [{ city: "北京" }, { city: "上海" }]
});
// 数组操作符
db.products.find({ tags: { $all: ["phone", "apple"] } });
db.users.find({ hobbies: { $size: 3 } });
// 正则
db.users.find({ email: /@gmail\.com$/ });02. 投影、排序、分页
find 的第二个参数叫投影——控制返回哪些字段。1 表示要,0 表示不要。下划线 id 比较特殊:默认要返回,想不返回得显式写 _id: 0。
排序用 sort(),1 是升序,-1 是降序。可以按多个字段排序。
分页用 skip() 和 limit() 配合——skip 跳过前 N 条,limit 限制返回条数。但注意 skip 大值时性能差,因为 MongoDB 还是要遍历跳过的文档。更优的方案是用游标(基于 _id 或某个排序字段做断点续查)。
countDocuments() 统计符合条件的文档数,可以配合查询条件精确计数。
javascript
// 投影
db.users.find({}, { name: 1, age: 1, _id: 0 });
// 排序
db.users.find().sort({ age: -1 }); // 年龄降序
db.users.find().sort({ city: 1, age: -1 }); // 先按城市升序再按年龄降序
// 分页
db.users.find().skip(20).limit(10); // 第 3 页,每页 10 条
// 游标分页(更好)
db.users.find({ _id: { $gt: lastId } }).limit(10);
// 计数
db.users.countDocuments({ age: { $gt: 30 } });skip 大值时性能差,因为 MongoDB 还是要遍历跳过的文档。用基于 _id 的游标分页更好。
03. 嵌套文档与数组查询
MongoDB 的嵌套文档查询用点号访问:address.city。数组查询更灵活——可能是数组里某个元素匹配、数组包含某个值、或者数组里满足多个条件。
查询数组有几种常见方式:直接匹配整个数组(顺序也必须一致)、美元 in 只要数组包含某个值、美元 all 数组包含所有指定值、美元 elemMatch 数组里至少一个元素满足组合条件。
嵌套的数组里再嵌套文档,查询就一层层用点号加操作符往下钻。但层级太深会影响性能和可读性,建议控制在 3 层以内。
javascript
// 嵌套字段
db.users.find({ "address.city": "北京" });
// 数组包含某个值
db.users.find({ hobbies: "golf" });
// 数组包含任意一个
db.users.find({ hobbies: { $in: ["golf", "tennis"] } });
// 数组包含所有
db.users.find({ hobbies: { $all: ["golf", "swimming"] } });
// 数组里元素满足组合条件
db.orders.find({
items: {
$elemMatch: { product: "iPhone", qty: { $gte: 2 } }
}
});美元 elemMatch 比嵌套的逗号条件更精确。不加美元 elemMatch 时,多个条件只要在数组里任意元素分别满足就行。
04. 游标方法与查询优化
find 返回的是一个游标对象,可以在上面链式调用各种方法:sort、limit、skip、hint(指定用哪个索引)、explain(查看执行计划)、count、toArray(转为数组)。
explain 是查询优化的利器——类似 MySQL 的 EXPLAIN。explain('executionStats') 能看到查询时间、扫描了多少文档、走了哪个索引。如果发现 totalDocsExamined 远大于 nReturned,说明扫了大量无关文档,可能需要优化索引。
hint 可以强制指定用哪个索引,在优化器选错索引时有用。但一般让优化器自己选,不要过度使用 hint。
javascript
// 链式调用
db.users.find({ age: { $gt: 20 } })
.sort({ age: -1 })
.skip(10)
.limit(5);
// 查看执行计划
db.users.find({ email: "[email protected]" }).explain("executionStats");
// 强制使用某个索引
db.users.find({ email: "[email protected]" }).hint({ email: 1 });
// 转为数组
const users = db.users.find().limit(10).toArray();explain('executionStats') 里的 totalDocsExamined 如果远大于 nReturned,说明索引不够好或没走索引。
05. 常用聚合查询快速入门
虽然聚合管道有专门的章节,但一些简单的聚合查询属于日常操作。countDocuments 数数、distinct 去重、estimatedDocumentCount 估算总数(用元数据,秒出结果)。
aggregate 也可以做简单聚合,比如分组计数、求和。虽然功能远不止这些,但简单的美元 group 加美元 sum 经常用到。
美元 match 相当于 find 的条件过滤,美元 group 相当于 SQL 的 GROUP BY,美元 sort 排序,美元 limit 限制数量。管道就是把这些操作串起来,上一个操作的输出是下一个操作的输入。
javascript
// distinct——去重
db.users.distinct("city");
// 快速估算总数
db.users.estimatedDocumentCount();
// 简单聚合——每个城市多少人
// 注意:这里只是演示概念,完整聚合见聚合管道章节
db.users.aggregate([
{ $group: { _id: "$city", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
]);distinct 对单个字段去重,返回数组。数据量大的时候有 16MB 限制(返回结果不能超过 16MB)。
知识测验
第 1/5 题正确 0
美元 in 和美元 all 的区别?
下一节
下一节 聚合管道