MongoDB 6.0文档结构转换求助:$unwind和$group未达预期结果
MongoDB 文档结构转换方案
原始文档结构
[ { "_id" : ObjectId("6555a7d7c2d9de74abb09de8"), "tagName" : "tag1", "tagValue" : "value1", "categories" : ["Category-1"], "otherField1" : [], "otherField2" : "something-else-1" }, { "_id" : ObjectId("6555a865c2d9de74abb09de9"), "tagName" : "tag2", "tagValue" : "value2", "categories" : ["Category-1", "Category-2"], "otherField1" : [], "otherField2" : "something-else-2" }, { "_id" : ObjectId("6555c652c2d9de74abb09df2"), "tagName" : "tag3", "tagValue" : "value3", "categories" : ["Category-2", "Category-3"], "otherField1" : [], "otherField2" : "something-else-3" } ]
目标结构
[ { "category" : "Category-1", "tags" : [ {"name" : "tag1", "value" : "value1"}, {"name" : "tag2", "value" : "value2"} ] }, { "category" : "Category-2", "tags" : [ {"name" : "tag2", "value" : "value2"}, {"name" : "tag3", "value" : "value3"} ] }, { "category" : "Category-3", "tags" : [{"name" : "tag3", "value" : "value3"}] } ]
MongoDB 聚合查询
通过以下聚合管道可实现结构转换,核心是先展开分类数组,再按分类分组收集标签:
db.collection.aggregate([ // 展开categories数组,每个分类生成一条独立文档 { $unwind: "$categories" }, // 按分类分组,收集对应标签 { $group: { _id: "$categories", tags: { $push: { name: "$tagName", value: "$tagValue" } } } }, // 重命名_id为category,并移除原_id字段 { $project: { _id: 0, category: "$_id", tags: 1 } } ])
C#/.NET 7 驱动实现代码
定义数据实体类
// 原始文档实体 public class TagDocument { public ObjectId Id { get; set; } public string TagName { get; set; } public string TagValue { get; set; } public List<string> Categories { get; set; } public List<object> OtherField1 { get; set; } public string OtherField2 { get; set; } } // 聚合结果实体 public class CategoryTagGroup { public string Category { get; set; } public List<TagItem> Tags { get; set; } } public class TagItem { public string Name { get; set; } public string Value { get; set; } }
聚合查询代码(两种实现方式)
方式一:基于BsonDocument构建管道
using MongoDB.Bson; using MongoDB.Driver; using System.Collections.Generic; using System.Threading.Tasks; public async Task<List<CategoryTagGroup>> GetCategoryTagsAsync(IMongoCollection<TagDocument> collection) { var pipeline = new BsonDocument[] { new BsonDocument("$unwind", "$Categories"), new BsonDocument("$group", new BsonDocument { ["_id"] = "$Categories", ["tags"] = new BsonDocument("$push", new BsonDocument { ["name"] = "$TagName", ["value"] = "$TagValue" }) }), new BsonDocument("$project", new BsonDocument { ["_id"] = 0, ["category"] = "$_id", ["tags"] = 1 }) }; return await collection.Aggregate<CategoryTagGroup>(pipeline).ToListAsync(); }
方式二:强类型聚合构建器(更易读)
using MongoDB.Driver; using System.Collections.Generic; using System.Threading.Tasks; public async Task<List<CategoryTagGroup>> GetCategoryTagsStronglyTypedAsync(IMongoCollection<TagDocument> collection) { var aggregation = collection.Aggregate() .Unwind(doc => doc.Categories) .Group( doc => doc.Categories, g => new CategoryTagGroup { Category = g.Key, Tags = g.Select(tagDoc => new TagItem { Name = tagDoc.TagName, Value = tagDoc.TagValue }).ToList() } ); return await aggregation.ToListAsync(); }
内容的提问来源于stack exchange,提问作者Bhanu
相关产品推荐
相关产品推荐

