You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 15:32:11