$max和$min不能直接用于数组字段求极值,因它们将整个数组视为单个bson值比较;需用$reduce(推荐)或$sort+$arrayelemat组合实现逐元素计算。

为什么 $max 和 $min 不能直接用在数组字段上?
直接对数组字段(比如 prices: [19.99, 24.5, 15.0])在 $project 阶段写 {$max: "$prices"} 是合法语法,但结果往往不是你想要的——它会把整个数组当做一个值参与比较(即按 BSON 类型顺序比),而不是逐元素取最大。真正想“从数组里找最大数字”,必须先“展开”数组。
根本原因:MongoDB 的 $max 和 $min 是**聚合组内累计算子**(常配合 $group),或用于**表达式上下文中的逐字段比较**;它们不自带“遍历数组元素”的语义。要提取数组极值,得靠 $reduce 或 $arrayElemAt + $sort 组合。
用 $reduce 提取数组最大/最小值(推荐)
$reduce 是最直接、最可控的方式,适合任意数值数组,且不改变原始文档结构。它显式定义累加逻辑,避免隐式排序开销。
- 对最大值:初始值设为
$$value的第一个元素(用$arrayElemAt: ["$arr", 0]),迭代时用$cond比较当前累加值和下一个元素,取大者 - 对最小值:同理,取小者即可
- 注意处理空数组:可在
$reduce前用$cond判断$size,避免$arrayElemAt返回null
{
$project: {
maxPrice: {
$reduce: {
input: "$prices",
initialValue: { $arrayElemAt: ["$prices", 0] },
in: { $cond: [{ $gt: ["$$this", "$$value"] }, "$$this", "$$value"] }
}
},
minPrice: {
$reduce: {
input: "$prices",
initialValue: { $arrayElemAt: ["$prices", 0] },
in: { $cond: [{ $lt: ["$$this", "$$value"] }, "$$this", "$$value"] }
}
}
}
}
用 $sort + $arrayElemAt 替代(简单但有代价)
如果数组不大($sort 配合 $arrayElemAt 快速取头尾。但注意:这是在内存中对数组做升序/降序排列,对大数组性能差,且无法在 $project 内联完成(需先 $addFields 再 $project)。
- 取最大值:先
$sort: -1得到降序数组,再$arrayElemAt: [.., 0] - 取最小值:先
$sort: 1升序,再$arrayElemAt: [.., 0] - 必须包裹在
$let或额外 stage 中,因为$sort是数组操作符,不能直接嵌套进$project表达式
{
$addFields: {
sortedPrices: { $sortArray: { input: "$prices", sortBy: -1 } }
}
},
{
$project: {
maxPrice: { $arrayElemAt: ["$sortedPrices", 0] }
}
}
别误用 $max/$min 在 $group 阶段
如果你看到文档里写 $max: "$prices" 出现在 $group 里,那它其实是在**跨文档比较**:对每个分组,取所有文档该字段(prices 数组整体)的最大 BSON 值,不是数组内部最大数。这通常毫无意义,还容易掩盖数据异常(比如一个文档存了 [100],另一个存了 [1,2,3],$max 会返回 [100],因为数组比数字类型高)。
- 真正想跨文档取某个数组字段的极值(如“所有商品最高单价”),应先
$unwind数组,再$group+$max - 错误写法:
{$group: {_id: null, globalMax: {$max: "$prices"}}}→ 返回的是数组,不是数字 - 正确写法:
{$unwind: "$prices"}, {$group: {_id: null, globalMax: {$max: "$prices"}}}
真正难的不是写出表达式,而是判断该不该展开数组、要不要处理空值、以及是否意识到 $max 在不同上下文里的语义完全不一样。多数人卡住,是因为默认它像 JavaScript 的 Math.max(...arr) —— 但它不是。











