无法将 'MongoDB.Bson.BsonString' 类型的对象转换为 MongoDB .NET 驱动程序中的类型 'MongoDB.Bson.BsonDocument'

Unable to cast object of type 'MongoDB.Bson.BsonString' to type 'MongoDB.Bson.BsonDocument' in MongoDB .NET Driver

我在尝试使用 MongoDB .NET 客户端 运行 聚合管道时遇到问题。我的代码如下所示:

    public async Task<IEnumerable<string>> GetPopularTags(int count)
    {
        var events = _database.GetCollection<Event>(_eventsCollectionName);

        var agg = events.Aggregate();
        var unwind = agg.Unwind<Event, Event>(e => e.Tags);
        var group = unwind.Group(e => e.Tags, v => new { Tag = v.Key, Count = v.Count() });
        var sort = group.SortByDescending(e => e.Count);
        var project = group.Project(r => r.Tag);
        var limit = project.Limit(count);
        var result = await limit.SingleOrDefaultAsync();

        return result;
    }

(每个阶段的单独变量仅用于调试目的)

在尝试获取管道结果(最后一个变量)时出现以下错误:

System.InvalidCastException: Unable to cast object of type 'MongoDB.Bson.BsonString' to type 'MongoDB.Bson.BsonDocument'

我错过了什么?

在此先感谢您的帮助!

解决方案

我终于弄明白,我在最后一行遇到异常与错误所在无关。我在每个阶段都尝试 运行ning .SingleOrDefault() 来查看输出,我注意到我的管道有几个问题。

  1. 我的展开阶段试图 return 一个 Event 对象,但由于它正在展开 Tags 属性(这是一个 List<string>),它试图将其设置为 string 并抛出异常。我通过让它将输出类型设置为默认值 BsonDocument 解决了这个问题,然后在下一阶段使用 ["Tags"] 访问器来获取我需要的值。它看起来像这样:

    var dbResult = await events.Aggregate()
        .Unwind(e => e.Tags)
        .Group(e => e["Tags"], v => new { Tag = v.Key, Count = v.Count() })
    
  2. 我的项目阶段由于某种原因无法正常工作。我无法将 Tag 属性(原来是 BsonValue 类型)转换为 string。最后我删除了那个阶段并将其替换为 dbResult.Select(t => t.Tag.AsString) 以将其转换为字符串。不是最优雅的解决方案,但总比没有好。

最后我的代码看起来像这样:

    public async Task<IEnumerable<string>> GetPopularTags(int count)
    {
        var events = _database.GetCollection<Event>(_eventsCollectionName);

        var dbResult = await events.Aggregate()
        .Unwind(e => e.Tags)
        .Group(e => e["Tags"], v => new { Tag = v.Key, Count = v.Count() })
        .SortByDescending(e => e.Count)
        .Limit(count)
        .ToListAsync();

        var result = dbResult.Select(t => t.Tag.AsString);

        return result;
    }

您遇到的问题基本上可以简化为以下代码行:

var agg = collection.Aggregate().Project(x => x.Tag);

其中 Tag 是模型中的 string 属性。

Aggregate() 和所有 MongoDB 驱动程序运算符似乎比 C# 语法允许的更接近聚合框架。

根据您的代码,result 变量应该是 String 类型,驱动程序将其转换为 MongoDB.Bson.BsonString 但是聚合框架总是 returns BSON文档(在本例中为单个文档),因此 MongoDB .NET 驱动程序无法在 运行 时间内处理此类反序列化 (BsonDocument -> BsonString).

第一个解决方法很明显 - return 任何类似于 BSON 文档并且可以从 BsonDocument 类型反序列化的东西,如:

collection.Aggregate().Project(x => new { x.Tag });

然后将结果映射到内存中(相同的查询在幕后 运行)

另一种方法:使用 .AsQueryable() 将您的查询转换为 LINQ,这允许您以更灵活的方式 return 结果:

collection.AsQueryable().Select(x => x.Tag);

在这两种情况下,为我的投影生成的查询看起来是一样的:

{aggregate([{ "$project" : { "Tag" : "$Tag", "_id" : 0 } }])}

有点晚了,但这也有类似的问题,这对我来说已经解决了:

您需要创建一个中间 class 来代表组 { Tag = v.Key, Count = v.Count() },然后将项目更改为此。

.Project(Builders<YourIntermediateClass>.Projection.Expression(x => x.Tag))