首选聚合管道的$group阶段查最值,用$_id: null全集分组,$max/$min自动忽略null/缺失值;sort().limit(1)易因无索引或复杂条件性能差;子集最值需先$match后$group;非数字类型按特定规则比较。

直接用 $group + $max/$min 最稳妥
查整个集合的某个字段最大值或最小值,首选聚合管道的 $group 阶段。它不依赖排序、不加载全部文档到内存,性能稳定,语义清晰。
-
db.collection.aggregate([{$group: {_id: null, maxVal: {$max: "$field"}, minVal: {$min: "$field"}}}])—— 用_id: null表示全集分组,一次返回两个值 - 字段名必须带
$前缀(如"$age"),写成"age"会当作字面量字符串,结果恒为 null - 如果字段存在
null或缺失值,$max/$min默认忽略它们;若需包含,得先用$ifNull处理
sort().limit(1) 看似简单,但有隐藏陷阱
用 find().sort({field: -1}).limit(1) 拿最大值,代码短,但实际使用中容易出问题。
- 没建索引时,
sort触发 collection scan,数据量稍大(比如 >10 万)就明显变慢 - 即使有索引,
sort+limit仍要扫描索引条目直到找到第一条,而$max可直接跳到 B-tree 末端节点,本质更高效 - 查询条件加在
find({})里没问题,但如果条件复杂(比如嵌套字段、数组展开),sort可能无法命中索引,$group则不受影响
查“满足条件的子集”里的最值,必须用 $group
比如“查 status 为 active 的用户中 age 的最大值”,不能靠 find({status: "active"}).sort(...) 简单解决——因为 sort 是对结果排序,不是对筛选后数据做聚合。
- 正确写法:
db.users.aggregate([{$match: {status: "active"}}, {$group: {_id: null, maxAge: {$max: "$age"}}}]) -
$match必须放在$group前面,否则先分组再过滤,逻辑错误 - 如果还要同时返回对应文档的
_id或其他字段,$max本身不带上下文,得换用$top(5.2+)或$reduce配合$cond手动实现
注意 $min/$max 对非数字类型的处理
它们支持字符串、日期、ObjectID 比较,但规则和直觉可能不一致。
- 字符串按字典序比较:
"10""2"(因为 '1' - 日期和 ObjectID 比较是安全的,MongoDB 内部有明确排序规则
- 混合类型(比如一个文档
score: 95,另一个score: "N/A")会被跳过,不会报错,但结果可能漏数据
$min 和 $max 是原子操作,不依赖外部排序逻辑,也不受文档顺序影响——这点比手写 sort/limit 更可靠,尤其在分布式或分片集群环境下。

















