MongoDB聚合分组如何实现formName-年份-月份层级嵌套数组结构
解决方案
完整聚合语句
db.FormsStore.aggregate([ // 保留原有过滤条件 {$match:{myKey:"a guid to group my forms together"}}, // 统一不同名的日期字段,转换为标准日期类型 { $addFields: { updateDate: { $dateFromString: { dateString: { $ifNull: ["$updatedAt", "$lastUpdated"] } } } } }, // 第一重分组:按表单名+年份+月份去重,避免重复的年月数据 { $group: { _id: { formName: "$formName", year: { $year: "$updateDate" }, month: { $month: "$updateDate" } } } }, // 可选:按年份、月份升序排列,输出的层级会按时间顺序展示 { $sort: { "_id.year": 1, "_id.month": 1 } }, // 第二重分组:按表单名+年份聚合,生成月份层级的子节点 { $group: { _id: { formName: "$_id.formName", year: "$_id.year" }, children: { $push: { text: "$_id.month" } } } }, // 第三重分组:按表单名聚合,生成年份层级的子节点 { $group: { _id: "$_id.formName", children: { $push: { text: "$_id.year", children: "$children" } } } }, // 格式化输出为要求的结构 { $project: { _id: 0, text: "$_id", children: 1 } } ])
核心逻辑说明
- 用
$ifNull兼容两种不同的日期字段名,统一转换为日期类型的updateDate字段,解决字段名不一致的问题 - 采用从细到粗的三层分组逻辑,先聚合最细粒度的「表单+年+月」,再依次向上生成年份、表单层级的结构,不会出现年月数据错位的问题
- 可选的
$sort步骤可以保证输出的年份、月份都是按时间顺序排列,不需要后续二次处理
C# 调用说明
使用官方MongoDB.Driver时,可以直接将上述管道每一步转为BsonDocument对象传入Aggregate方法即可,示例参考:
var pipeline = new BsonDocument[] { BsonDocument.Parse("{ $match:{myKey:\"a guid to group my forms together\"} }"), // 依次填入其他管道步骤的BsonDocument即可 }; var result = await mongoDatabase.GetCollection<BsonDocument>("FormsStore") .AggregateAsync<BsonDocument>(pipeline);
也可以直接用LINQ语法构造对应分组逻辑,和原生聚合的执行效率没有差异。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

