使用MongoDB .NET Driver实现数据分组并返回关联文档数据的技术问题
解决MongoDB .NET Driver v2.13中$addToSet聚合操作的错误
嘿,我一眼就看出你代码里的问题了——你把$addToSet的结构搞反了!在MongoDB的聚合$group阶段,字段名应该是你要输出的键(比如names),而$addToSet是这个字段对应的操作符,你之前把$addToSet当成了键,这就导致驱动识别不了这个合法的聚合操作,所以才会报错。
错误原因拆解
你原来的代码里,$addToSet被写成了$group对象的键:
{ "$addToSet", new BsonDocument { {"names", "$name"}, } }
这完全不符合MongoDB聚合的语法规范,正确的结构应该是输出字段名作为键,值是包含$addToSet操作的文档。
修正后的BsonDocument写法
直接调整$group内的结构,把names作为字段键,$addToSet作为操作符放在值里:
var group = new BsonDocument { { "$group", new BsonDocument { { "_id", new BsonDocument { { "grade", "$grade" } } }, { "count", new BsonDocument { { "$sum", 1 } } }, // 正确结构:字段名是names,值是$addToSet操作 { "names", new BsonDocument { { "$addToSet", "$name" } } } } } }; var pipeline = new[] { group }; await col.Aggregate<BsonDocument>(pipeline) .ForEachAsync(doc => { // 处理结果,比如打印输出 Console.WriteLine(doc.ToJson()); });
更简洁的AggregateBuilder写法
如果你觉得BsonDocument写起来容易出错,推荐使用MongoDB .NET Driver的AggregateBuilder,它的强类型写法更直观:
// 假设你的集合实体类名为Student(如果没有实体类,也可以用BsonDocument) var pipeline = col.Aggregate() .Group( // 按grade字段分组 key => key["grade"].AsString, // 定义分组后的输出结构 group => new { grade = group.Key, // ToHashSet()对应MongoDB的$addToSet操作 names = group.Select(s => s["name"].AsString).ToHashSet(), // Count()对应$sum:1 count = group.Count() } ); await pipeline.ForEachAsync(result => { Console.WriteLine($"Grade: {result.grade}, Names: {string.Join(", ", result.names)}, Count: {result.count}"); });
优化输出结构(可选)
如果想把_id.grade直接改成顶级的grade字段,去掉默认的_id,可以在聚合pipeline里再加一个$project阶段:
var project = new BsonDocument { { "$project", new BsonDocument { { "grade", "$_id.grade" }, { "names", 1 }, { "count", 1 }, { "_id", 0 } // 隐藏_id字段 } } }; var pipeline = new[] { group, project };
这样就能得到完全和你期望一致的输出格式:
{ "grade": "A", "names": ["amy", "doug"], "count": 2 } { "grade": "B", "names": ["cathy"], "count": 1 } { "grade": "C", "names": ["edith", "frank"], "count": 2 } { "grade": "D", "names": ["bob"], "count": 1 }
内容的提问来源于stack exchange,提问作者InfiniteLoop
相关产品推荐
相关产品推荐

