如何在MongoDB中同时获取多字段的argmax/argmin并返回对应uid
解决MongoDB多字段同时执行argmax/argmin并返回对应uid的问题
嗨,我来帮你搞定这个MongoDB的聚合查询问题!我看你已经能单独处理单个字段的argmax/argmin了,咱们只需要把这些逻辑整合到一个聚合管道里,就能同时处理多个字段啦。
你的数据示例
首先先明确你的输入数据:
[ { "uid": "111", "a": 1, "b": 3, "c": 1 }, { "uid": "222", "a": 2, "b": 2, "c": 2 }, { "uid": "333", "a": 3, "b": 1, "c": 3 } ]
需求回顾
你需要:
- 对字段
a执行argmax,返回对应uid(即a最大值3对应的333) - 对字段
b执行argmax,返回对应uid(即b最大值3对应的111) - 对字段
c执行argmin,返回对应uid(即c最小值1对应的111) - 最终得到格式:
[ { "argmax_of_a": "333", "argmax_of_b": "111", "argmin_of_c": "111" } ]
完整聚合查询方案
这里用MongoDB的聚合管道来实现,核心思路是先把所有文档聚合到一个数组中,然后分别针对每个字段计算极值并匹配对应的uid:
db.collection.aggregate([ // 第一步:把所有文档收集到一个数组中 { $group: { _id: null, docs: { $push: "$$ROOT" } } }, // 第二步:分别计算每个字段的极值,并匹配对应的uid { $project: { _id: 0, // 去掉默认的_id字段 argmax_of_a: { $let: { vars: { maxA: { $max: "$docs.a" }, // 计算a字段的最大值 // 筛选出a等于最大值的文档,取第一个匹配项 matchedDoc: { $arrayElemAt: [{ $filter: { input: "$docs", cond: { $eq: ["$$this.a", "$$maxA"] } } }, 0] } }, in: "$$matchedDoc.uid" // 提取匹配文档的uid } }, argmax_of_b: { $let: { vars: { maxB: { $max: "$docs.b" }, // 计算b字段的最大值 matchedDoc: { $arrayElemAt: [{ $filter: { input: "$docs", cond: { $eq: ["$$this.b", "$$maxB"] } } }, 0] } }, in: "$$matchedDoc.uid" } }, argmin_of_c: { $let: { vars: { minC: { $min: "$docs.c" }, // 计算c字段的最小值 matchedDoc: { $arrayElemAt: [{ $filter: { input: "$docs", cond: { $eq: ["$$this.c", "$$minC"] } } }, 0] } }, in: "$$matchedDoc.uid" } } } } ])
代码解释
- $group阶段:通过
_id: null将所有文档归为一组,并用$push: "$$ROOT"把完整的文档存入docs数组,这样后续可以统一对这个数组进行多次筛选操作。 - $project阶段:
- 用
$let来封装每个字段的逻辑,让代码更清晰; - 先用
$max/$min计算目标字段的极值; - 再用
$filter从docs数组中筛选出字段值等于极值的文档,并用$arrayElemAt取出第一个匹配的文档(如果存在多个极值对应的文档,你可以根据需求调整,比如返回所有匹配的uid数组); - 最后提取出匹配文档的
uid作为结果字段的值; - 用
_id: 0去掉默认生成的_id字段,得到你想要的简洁格式。
- 用
结果验证
运行这个聚合管道后,得到的结果正好符合你的要求:
[ { "argmax_of_a": "333", "argmax_of_b": "111", "argmin_of_c": "111" } ]
额外说明
如果你的数据量非常大,这种把所有文档加载到内存的方式可能会有性能问题,这时候可以考虑分阶段单独计算每个字段的极值再合并,但对于大多数常规规模的数据来说,这个方案简单直接,容易理解和维护。
内容的提问来源于stack exchange,提问作者kaixin liu
相关产品推荐
相关产品推荐

