MongoDB的查询文档是使用频率最高的功能之一,无论是日常取数、接口开发还是数据分析,几乎都绕不开它。所谓查询文档,就是传给find等方法的那段JSON结构,用来描述筛选条件。看似简单,实际上隐藏着不少细节:条件怎么组合、数组怎么匹配、内嵌文档怎么定位、什么情况下索引会失效,这些都会直接影响查询结果的正确性和性能。本文从基础语法讲起,逐步深入运算符、数组查询和常见陷阱,帮助你完整掌握MongoDB查询文档的正确写法。

一、find方法与查询文档的基本结构
MongoDB中最基础的查询方法是db.collection.find(query, projection),其中第一个参数就是查询文档,第二个参数是投影文档,用于指定返回哪些字段。当查询文档为空对象时,会返回集合中的所有文档,效果等同于全表扫描。
查询文档的基本形式是键值对,例如{name: "张三"}表示匹配name字段等于张三的所有文档。多个键值对并列写在同一个对象里时,默认是AND关系,也就是所有条件都要满足。这一点和SQL中的WHERE子句加AND类似,但写法上更容易被忽略。
// 查询user集合中age为25的所有文档
db.user.find({age: 25})
// 多条件AND:name为张三且age为25
db.user.find({name: "张三", age: 25})
// 使用投影只返回name和age字段,_id默认返回,可用0排除
db.user.find({age: 25}, {name: 1, age: 1, _id: 0})
除了find之外,还有findOne用于返回单个文档,countDocuments用于统计数量,它们的第一个参数同样是查询文档,语法完全一致。掌握一套查询文档写法,就能在这些方法之间通用,这也是MongoDB API设计比较友好的地方。
二、常用查询运算符详解
仅靠等值匹配显然不够,MongoDB提供了丰富的查询运算符,都以$开头写在字段值的位置。最常用的是比较运算符:$gt大于、$gte大于等于、$lt小于、$lte小于等于、$ne不等于、$in在指定集合中、$nin不在指定集合中。
// 查询年龄在18到30之间(含边界)的文档
db.user.find({age: {$gte: 18, $lte: 30}})
// 查询城市为北京或上海的文档
db.user.find({city: {$in: ["北京", "上海"]}})
// 逻辑运算符:$or满足任一条件即可
db.user.find({$or: [{age: {$lt: 18}}, {age: {$gt: 60}}]})
// $not取反,查询age不大于30的文档
db.user.find({age: {$not: {$gt: 30}}})
逻辑运算符包括$and、$or、$nor和$not。当需要对同一个字段施加多个条件时,直接在同一对象中并列写运算符即可,比如上面的年龄区间查询;当需要对不同字段做OR关系,就必须显式使用$or并传入数组。
还有一类字段存在性运算符很实用:$exists判断字段是否存在,$type判断字段类型。在字段结构不固定的集合中,先用$exists过滤掉缺少字段的文档,可以避免很多意外的空值问题。
三、数组查询与内嵌文档查询
MongoDB的文档经常包含数组字段,数组查询的规则和普通字段有明显差异。直接用等值匹配数组时,只有数组内容和查询条件完全一致(包括元素顺序)才会命中;而如果条件是数组中的单个元素,MongoDB会匹配包含该元素的数组。
// tags数组中同时包含mongodb和database(顺序无关,$all语义)
db.article.find({tags: {$all: ["mongodb", "database"]}})
// $size匹配指定长度的数组,注意它不能配合范围查询
db.article.find({tags: {$size: 3}})
// $elemMatch:数组元素需同时满足多个条件
db.score.find({results: {$elemMatch: {math: {$gte: 90}, english: {$gte: 85}}}})
$elemMatch是数组查询中最重要的运算符。假设成绩数组中每个元素包含数学和英语两个字段,如果写成{results: {math: {$gte: 90}, english: {$gte: 85}}}的普通内嵌写法,语义会变成数组元素整体匹配这个文档结构,结果往往不符合预期。而$elemMatch明确要求同一个数组元素同时满足两个条件,这才是业务上通常想要的含义。
内嵌文档查询推荐使用点号路径写法,例如{address.city: "北京"}表示匹配address内嵌对象中city为北京的文档。如果直接用完整对象匹配{address: {city: "北京", street: "中关村"}},则要求内嵌文档与查询条件完全相同,字段多一个或少一个都不行。点号写法更灵活,也更容易利用索引。
四、常见坑与避坑建议
第一个坑是隐式AND带来的误解。多个条件并列时默认是AND关系,有些开发者以为可以借此实现OR,结果查出来的数据为空。涉及或关系的查询,务必显式写$or。
第二个坑是类型匹配问题。MongoDB的$gt、$lt等比较运算符是区分BSON类型的,字符串"10"和数字10不会互相匹配。如果集合中同一字段混存了字符串和数字,查询结果会出现看似随机的遗漏,建议入库前统一字段类型。
第三个坑是正则表达式和大小写。字符串等值匹配默认区分大小写,"Tom"和"tom"不相等。需要忽略大小写时可用$regex配合$options: "i",但前缀未锚定的模糊正则无法有效利用索引,数据量大时性能会急剧下降,能精确匹配就尽量避免模糊查询。
// 忽略大小写查询name包含tom的文档
db.user.find({name: {$regex: /tom/, $options: "i"}})
// 前缀匹配可以走索引,后缀通配则不行
db.user.find({name: {$regex: /^Tom/}})
第四个坑是$ne、$nin、$not以及未加约束的$exists这类否定条件,它们通常无法高效利用索引,容易触发全集合扫描。在数据量大的集合上应尽量改写为正向条件,或者通过合理的索引设计来缓解。
最后一个建议是养成用explain检查查询计划的习惯。db.user.find({...}).explain("executionStats")可以查看是否命中索引、扫描了多少文档、耗时多少。上线前的关键查询都过一遍explain,是避免线上性能事故最有效的手段。
五、总结
MongoDB查询文档的核心在于准确表达筛选条件并让条件能够命中索引。基础等值匹配和多条件AND是日常主力;比较与逻辑运算符覆盖绝大多数业务场景;数组查询牢记$elemMatch的语义,内嵌查询优先用点号路径;最后警惕类型不一致、大小写敏感和否定条件导致的索引失效,配合explain验证查询计划。把这些细节吃透,查询文档的编写就会既准确又高效。
MongoDB查询文档MongoDB find方法MongoDB查询运算符修改时间:2026-09-01 11:36:55