find 是什么
find 是 MongoDB 的文档查询入口:db.collection.find(filter, projection) 在集合上做条件过滤加字段投影,返回游标(cursor)。语义上对应 SQL 的 SELECT ... WHERE,但条件用 BSON 文档表达,天然支持嵌套字段、数组元素、正则与多种查询操作符。它是使用频率最高的读操作,count 与聚合管道里的 $match 都共用同一套条件语法。
基本信息
| 适用版本 | MongoDB 3.2 及以上;mongosh 2.x |
|---|---|
| 语法 | db.collection.find(filter, projection) |
| 返回值 | 游标,链式调用或迭代时才真正取数 |
| 权限 | 对应库的 read / readWrite 角色 |
| 默认行为 | 结果含 _id,mongosh 交互环境打印前 20 条 |
典型使用场景
- 按主键或唯一键取单条记录,替代逐条遍历
- 列表页多条件筛选:状态、时间区间、归属人组合
- 导出或批处理:配合 sort 与 limit 分批拉取大集合
- 排查数据问题:用投影只看关键字段,缩小输出体积
- 执行更新前采样:先 find 确认真实命中范围与文档结构
- 预演聚合逻辑:先用 find 验证 $match 条件是否写对
最小示例
最小示例
db.orders.find({ status: "paid", qty: { $gte: 2 } }) # 状态已支付且数量不小于 2
db.orders.find({ "customer.city": "Hangzhou" }) # 点号路径访问嵌套字段
db.orders.find({ tags: "vip" }) # 数组字段:任一元素等于 vip
db.orders.find({ sku: { $in: ["A-1", "A-2"] } }) # 多值任一命中
db.orders.find({ ts: { $gte: ISODate("2026-01-01T00:00:00Z") } })
db.orders.find({}, { sku: 1, qty: 1, _id: 0 }) # 只返回 sku 与 qty
db.orders.find().sort({ ts: -1 }).limit(10) # 最近 10 条常用查询操作符
| 操作符 | 含义 | 写法示例 |
|---|---|---|
| $eq / $ne | 等于 / 不等于 | { status: { $ne: "void" } } |
| $gt $gte $lt $lte | 数值与日期比较 | { qty: { $gt: 1, $lte: 9 } } |
| $in / $nin | 在 / 不在给定集合 | { sku: { $in: ["A-1", "A-2"] } } |
| $exists | 字段是否存在 | { remark: { $exists: true } } |
| $regex | 正则匹配,未锚定即子串匹配 | { sku: /^A-/ } |
| $and / $or / $nor | 逻辑组合 | { $or: [ { a: 1 }, { b: 1 } ] } |
| $elemMatch | 数组内多条件同时满足 | { items: { $elemMatch: { sku: "A-1", qty: { $gt: 3 } } } } |
投影与游标控制
投影与游标控制
db.orders.find({ status: "paid" }, { sku: 1, qty: 1, _id: 0 }) # 白名单投影
db.orders.find({ status: "paid" }, { items: 0 }) # 黑名单投影:排除字段
const c = db.orders.find({ status: "paid" }).batchSize(200) # 每批取 200 条
while (c.hasNext()) { printjson(c.next()) } # 手动迭代游标find() 只构造游标、不发送请求;mongosh 交互环境在回车时自动迭代前 20 条,脚本里必须显式 toArray() 或循环迭代才会取数。
投影的指定字段与排除字段不能混用(只有 _id 例外);只取需要的字段能明显减少网络传输,但对磁盘读取量影响有限。
无过滤条件的 find() 等价于把整个集合拉回客户端,务必配合条件或 limit 使用;宁可先 count 确认规模再决定读法。
集合不存在时 find 返回空结果而不是报错;怀疑查错库表时用 show collections 与 db.getCollectionNames() 核对命名空间。
查询性能要点
- 条件字段与索引前缀一致时才能用上索引,复合索引的字段顺序要与查询字段对齐
- 只需要索引内字段时构成覆盖查询,无需回读文档,是速度最快的一类查询
- 排序字段没有匹配索引时会走内存排序,大结果集上开销明显
- 用 hint() 可以强制指定索引,验证执行计划差异时很有用
- 把高选择性条件(唯一键、时间范围)放前面,能更快缩小候选集
- 用 explain 观察 totalDocsExamined 与 nReturned 的比值,比值接近 1 才算高效
验证查询计划
验证查询计划
db.orders.find({ status: "paid" }).explain("executionStats") # 看 winningPlan 与扫描量
db.orders.find({ sku: "A-1" }).hint({ sku: 1 }) # 强制走指定索引
db.orders.find({ sku: "A-1" }, { _id: 0, sku: 1 }).explain() # 检查是否覆盖查询find 条件里字段的书写顺序不影响索引选择,但复合索引的字段顺序必须与查询用到的字段前缀一致,否则只能部分利用索引。
对同一字段同时写 $gt 与 $lt 属于范围查询,复合索引中范围字段之后的字段通常无法再用于索引过滤,设计索引时把等值条件字段放前面。
同一集合内各文档结构可以不同,查询条件只会命中含该字段的文档;当统计口径依赖某字段时先核对字段覆盖率,别把缺失字段当成 0 处理。