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

C# MongoDB如何返回匹配数组属性的对象且无需手动投影

解决方案:过滤嵌套集合并保留原对象结构

方案一:数据库端聚合过滤(无需手动映射属性)

使用MongoDB聚合管道的$filter操作符,结合AddFields阶段替换Subscriptions字段,自动保留其他所有属性,无需手动映射。

代码示例

var partnerId = "123";
var collection = mongoDatabase.GetCollection<DriverDocument>("DriverDocuments");

// 匹配包含符合条件订阅的文档
var matchFilter = Builders<DriverDocument>.Filter.ElemMatch(
    x => x.Subscriptions, 
    s => s.PartnerId == partnerId
);

// 用$filter筛选符合条件的订阅项,替换原Subscriptions字段
var filterStage = new BsonDocument("$addFields", new BsonDocument
{
    { 
        nameof(DriverDocument.Subscriptions), 
        new BsonDocument("$filter", new BsonDocument
        {
            { "input", $"${nameof(DriverDocument.Subscriptions)}" },
            { "as", "subscription" },
            { "cond", new BsonDocument("$eq", new BsonArray { "$$subscription.PartnerId", partnerId }) }
        })
    }
});

// 执行聚合查询并映射为强类型对象
var result = await collection.Aggregate()
    .Match(matchFilter)
    .AppendStage<DriverDocument>(filterStage)
    .ToListAsync();

这种方式在数据库端完成过滤,无需手动列出所有需要保留的属性,后续新增DriverDocument的属性时,完全不需要修改查询代码。

方案二:内存端过滤(简单易实现)

如果数据集不大,可以先查询出所有符合条件的文档,再在内存中过滤Subscriptions集合。这种方式代码更简洁,不需要使用任何BsonDocument。

代码示例

var partnerId = "123";
var collection = mongoDatabase.GetCollection<DriverDocument>("DriverDocuments");

// 查询所有包含目标PartnerId订阅的文档
var docs = await collection.Find(Builders<DriverDocument>.Filter.ElemMatch(
    x => x.Subscriptions, 
    s => s.PartnerId == partnerId
)).ToListAsync();

// 内存中过滤每个文档的Subscriptions
foreach (var doc in docs)
{
    doc.Subscriptions = doc.Subscriptions.Where(s => s.PartnerId == partnerId).ToList();
}

这种方式的缺点是会加载符合条件文档的完整Subscriptions集合到内存,数据量大时可能影响性能,但胜在实现简单,无需复杂的聚合逻辑。

封装通用聚合过滤方法(进阶)

如果需要频繁处理类似的嵌套集合过滤场景,可以封装通用方法,避免重复编写BsonDocument代码:

public static class MongoExtensions
{
    public static IAggregateFluent<T> FilterNestedArray<T, TItem>(
        this IAggregateFluent<T> aggregate,
        Expression<Func<T, IEnumerable<TItem>>> arraySelector,
        string filterFieldName,
        object filterValue)
    {
        var arrayFieldName = ((MemberExpression)arraySelector.Body).Member.Name;
        
        var filterStage = new BsonDocument("$addFields", new BsonDocument
        {
            { 
                arrayFieldName, 
                new BsonDocument("$filter", new BsonDocument
                {
                    { "input", $"${arrayFieldName}" },
                    { "as", "item" },
                    { "cond", new BsonDocument("$eq", new BsonArray { $"$$item.{filterFieldName}", filterValue }) }
                })
            }
        });
        
        return aggregate.AppendStage<T>(filterStage);
    }
}

使用示例

var result = await collection.Aggregate()
    .Match(matchFilter)
    .FilterNestedArray<DriverDocument, SubscriptionInfo>(
        x => x.Subscriptions, 
        nameof(SubscriptionInfo.PartnerId), 
        partnerId)
    .ToListAsync();

这个通用方法可复用在任何嵌套数组过滤场景,新增属性时完全不需要修改过滤逻辑。


内容的提问来源于stack exchange,提问作者Jon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 11:13:11