MongoDB C#:如何用Lookup关联集合实现单次查询投影?
问题描述
我有两个集合,分别为ColA和ColB,对应的C#实体类定义如下:
public sealed class ColA : IDatabaseObject { [BsonId] public Guid Id { get; set; } [BsonElement("colb_id")] public Guid ColB_Id { get; set; } }
public sealed class ColB : IDatabaseObject { [BsonId] public Guid Id { get; set; } [BsonElement("title")] public string Title { get; set; } }
我需要将ColA的结果投影到Proj类中,并通过ColA中的ColB_Id获取ColB的Title字段,Proj类定义如下:
public sealed class Proj { public Guid Id { get; set; } public Guid Colb_Id { get; set; } public string OtherTitleName { get; set; } }
我尝试了如下Lookup写法,但无法实现需求:
this.db.GetCollection<ColA>() .Aggregate() .Lookup<ColA, ColB, Proj>( this.db.GetCollection<ColB>(), a => a.ColB_Id, b => b.Id, // I need something like... a => new Proj { Id = a.Id, ColB_Id = a.ColB_Id, OtherTitleName = b.Title } )
请问是否可以通过Lookup实现该需求,还是必须分两次查询?
解决方案
可以通过Lookup结合后续聚合操作实现需求,不需要分两次查询。你当前的写法问题在于:Lookup默认会将匹配的ColB文档以数组形式返回,无法直接在Lookup的委托中访问单个ColB实例的字段,需要先展开数组再完成投影。
方法一:Lookup + Unwind + Project
var result = this.db.GetCollection<ColA>() .Aggregate() // 关联ColB集合,将匹配结果存入临时字段ColBData .Lookup<ColA, ColB, ColAWithColB>( foreignCollection: this.db.GetCollection<ColB>(), localField: a => a.ColB_Id, foreignField: b => b.Id, @as: a => a.ColBData ) // 展开ColBData数组(因是一对一关联,展开后仅单个元素) .Unwind(a => a.ColBData) // 投影到目标Proj类 .Project(a => new Proj { Id = a.Id, Colb_Id = a.ColB_Id, OtherTitleName = a.ColBData.Title }) .ToList(); // 临时中间类,用于承载关联后的ColB数据 public sealed class ColAWithColB : ColA { public List<ColB> ColBData { get; set; } }
方法二:使用带Pipeline的Lookup重载(MongoDB.Driver 2.12+)
如果使用较新版本的MongoDB驱动,可直接在Lookup中定义投影逻辑,无需额外临时类:
var result = this.db.GetCollection<ColA>() .Aggregate() .Lookup( foreignCollection: this.db.GetCollection<ColB>(), localField: a => a.ColB_Id, foreignField: b => b.Id, @as: "colBData", lookupOptions: new LookupOptions<ColA, ColB, Proj> { // 仅投影ColB的Title字段,减少数据传输 Pipeline = new EmptyPipelineDefinition<ColB>() .Project(b => new { b.Title }) } ) .Unwind("$colBData") .Project(a => new Proj { Id = a.Id, Colb_Id = a.ColB_Id, OtherTitleName = a.colBData.Title }) .ToList();
关键说明
- 无论哪种方法,
Unwind都是必要步骤:因为Lookup返回的是匹配文档数组,即使是一对一关联也会以数组形式存在,必须展开后才能访问单个ColB的字段。 - 如果存在ColB_Id无对应ColB文档的情况,可添加
Match过滤掉无匹配的记录,或者在投影时处理null值。
内容的提问来源于stack exchange,提问作者thibsc
相关产品推荐
相关产品推荐

