uniCloud聚合查询必须用.aggregate()而非.where(),因后者不支持.group()等操作;正确流程为.aggregate().match().addFields().group().project().end(),日期需先$toDate再$month,联表要用$.pipeline()和$$变量,.project()后字段名变更需注意引用。

uniCloud聚合查询必须用.aggregate(),不能用.where()
很多人写完db.collection('orders').where({ status: 'paid' }).group(...)发现报错,因为.where()返回的是查询对象,不支持.group().addFields()这些聚合操作符。聚合必须从.aggregate()开始构建流水线。
正确入口是:db.collection('orders').aggregate(),之后才能链式调用.match().addFields().group().project()等。
-
.match()相当于 SQL 的 WHERE,放在流水线开头能提前过滤,提升性能 -
.addFields()用于计算新字段(比如把时间戳转成月份),注意$toDate和$month必须配合使用 -
.group()的_id必须是对象或null,不能直接写字符串;分组键要带$前缀,如"$home_campus" - 最后一定要加
.end()才会真正执行,漏掉就啥也不返回
日期字段处理最容易出错:时间戳要先$toDate再$month
如果你的字段叫creat_date,值是毫秒级数字(如1701234567890),直接写$month: '$creat_date'会返回null——MongoDB 聚合里所有日期操作都要求输入是 Date 类型,不是数字。
必须两步走:
- 用
$toDate把数字转成 Date:{ $toDate: '$creat_date' } - 再用
$month提取月份:{ $month: { $toDate: '$creat_date' } }
常见错误写法:$month: '$creat_date' 或 $month: { $toDate: '$creat_date' * 1000 }(乘法在聚合里要用$multiply,不能直接写*)
联表+聚合要嵌套.pipeline(),不能直接.match()
想查“每个班级的平均分”,得先lookup关联学生表,再在学生数据上做.group()。但.lookup的pipeline参数里不能直接写.match({ class_id: '$id' })——这里的$id是外层 class 表的字段,必须用_.expr()包装,且匹配语法要写成$.eq(['$class_id', '$$id'])(双美元$$表示 pipeline 内部变量)。
典型结构:
db.collection('class').aggregate()
.lookup({
from: 'student',
let: { classId: '$id' },
pipeline: $.pipeline()
.match(_.expr($.eq(['$class_id', '$$classId'])))
.group({ _id: null, avgScore: $.avg('$score') })
.project({ _id: 0, avgScore: 1 }),
as: 'stats'
})
.end()漏掉$.pipeline()或写错$$变量名,都会导致联表结果为空数组。
.project()之后字段名会变,后续操作要按新名字引用
比如你用.project({ name: '$username', scoreLevel: { $cond: [...] } })重命名了字段,后面再.group()就得用$scoreLevel,不能再用$score——原始字段在.project()后已不可见。
特别注意.arrayElemAt(['$type_data', 0])这类操作:它把数组变成单个对象,之后$type_data.name就能直接取值,但如果你忘了.project这步,$type_data还是个数组,$type_data.name就是undefined。
调试技巧:在.end()前加一个.project({ _id: 0 })临时去掉_id,让返回结果更干净,方便肉眼核对字段结构。


















