【问题标题】:MongoDB C# Fluent AggregateMongoDB C# Fluent 聚合
【发布时间】:2020-05-04 19:16:53
【问题描述】:

我正在尝试使用具有流畅接口的聚合管道但没有成功,但我没有收到任何错误(结果中的所有字段均为空)。

我有这个 User 类:

public class User
{
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id;

    ...

    [BsonElement("last_access")]
    public DateTime LastAccess;
}

实体类:

public class Entity
{
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id;

    ...

    [BsonElement("active")]
    public bool Active;

    [BsonElement("user_id")]
    public string UserId;
}

UserLookup 类。这用于$lookup

class UserLookup
{
    public int EntityCount;

    public IEnumerable<User> UsersData;
}

UserResult 类。这用于分组和投影。

class UserResult
{
    public string UserId;

    public int EntityCount;

    public User UserData;
}

在我的函数中,我有这样的东西:

IMongoCollection<Entity> entityCol = Database.Instance.GetCollection<Entity>("entities");
IMongoCollection<User> usersCol = Database.Instance.GetCollection<User>("users");

IAsyncCursor<UserResult> result = entityCol.Aggregate()
    .Match(e => e.Active)
    .Group(e => e.UserId, g => new UserResult {
        UserId = g.Key,
        EntityCount = g.Count()
    })
    .Lookup<UserResult, User, UserLookup>(usersCol,
        lf => lf.UserId,  // localField. UserResult.UserId
        ff => ff.Id,      // foreignField. User.Id
        r => r.UsersData  // result. UserLookup.UsersData
    )
    .Project(p => new UserResult {
        UserId = p.UserId,
        EntityCount = p.EntityCount,
        UserData = p.UsersData.First()
    })
    .ToCursor();

while (result.MoveNext()) {
    foreach (var ur in result.Current) {
        // ur.UserId = null; ur.UserData = null; ur.EntityCount = 0;
    }
}

我没有收到任何错误,但EntityCount 始终为 0,UserIdUserData 均为空。基本上,我想要的是:

  1. 获取所有处于活动状态的实体 (Match)。
  2. 按用户 ID (Group) 对它们进行分组。
  3. 在用户集合中查找以获取用户数据 (Lookup)。
  4. 投影结果以返回一个包含实体计数和用户数据的简单对象 (Project)。

----- 更新 1

好的,在玩了mongo shell之后,我想我发现了问题。似乎 mongo 无法找到带有ObjectId 的 id 条目,只能使用字符串。这很奇怪,我找到了this answer,似乎可以使用 ObjectId 找到(至少在过去)。

在 mongo shell 中,如果我使用 db.users.find({ _id: ObjectId("...") }) 它不会返回任何内容,但使用 db.users.find({ _id: "..." }) 它会返回预期的用户。

我从头开始编写聚合查询以在 shell 上运行,这里是:

db.entities.aggregate([
    {
        $match: {
            "active": "true",
        }
    },
    {
        $group: {
            "_id" : {
                $toString: "$user_id"
            },
            "EntityCount": { "$sum" : 1 }
        }
    },
    {
        $lookup: {
            from: "users",
            localField: "_id",
            foreignField: "_id",
            as: "UsersData"
        }
    },
    {
        $project: {
            "_id": "$_id",
            "EntityCount": "$EntityCount",
            "UserData": {
                "$arrayElemAt": ["$UsersData", 0]
            },
        }
    },
    { $limit: 2 }
])

$group 阶段请注意,我将用户 ID 转换为字符串。如果我使用 "_id": "$user_id" 将无法工作。

最后一个阶段$limit 只是为了不炸毁控制台,从而更容易阅读结果。

这个查询执行得非常好。

返回 C#

这是 C# 驱动程序使用的最终查询:

[
    {
        "$match": {
            "active": true,
        }
    },
    {
        "$group": {
            "_id": "$user_id",
            "EntityCount": {
                "$sum":1
            }
        }
    },
    {
        "$lookup": {
            "from": "users",
            "localField": "_id",
            "foreignField": "_id",
            "as": "users_data"
        }
    },
    {
        "$project": {
            "UserId": "$user_id",
            "EntityCount": "$EntityCount",
            "UserData": {
                "$arrayElemAt": ["$user_data", 0]
            },
            "_id": 0
        }
    }
]

我不知道为什么,但在 $group 阶段,UserId 字段被忽略(这解释了为什么它在结果中始终为空)。此外,您可以注意到 _id$lookup 阶段被设置为 0。

我将字段 UserIdUserResult 重命名为 Id 并添加了属性 [BsonElement("_id")]

现在,我在结果中同时获得了用户 ID 和实体计数,但 UserData 仍然为空。

【问题讨论】:

  • 您是否建立了与 MongoDB 数据的 Mongodb 连接?你在使用 Newtonsoft.Json.Bson 和 MongoDB 库吗?
  • @IliassNassibane 连接正常。 MongoDB.Driver 的 nuget 版本是 2.9.2。我的 mongo 服务器是 4.2.3。我所有的操作都运行良好,除了这个聚合。

标签: c# mongodb aggregation-framework


【解决方案1】:

查询表单,有效>

IEnumerable<UserResult> result = entityCol.AsQueryable().Where(x => x.Active).ToLookup(x => x.UserId)
    .Select(x => new UserResult {EntityCount = x.Count(), UserId = x.Key}).Join(usersCol.AsQueryable(),
        x => x.UserId, x => x.Id,
        (userResult, user) => new UserResult
            {EntityCount = userResult.EntityCount, UserData = user, UserId = userResult.UserId});

foreach (var ur in result)
{
    // ur.UserId = null; ur.UserData = null; ur.EntityCount = 0;
}

您对 ObjectId 的怀疑 - 字符串转换在 Grouping 中不起作用是正确的。

这行得通:

public class User
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id;

    [BsonElement("last_access")]
    public DateTime LastAccess;
}

public class Entity
{
    [BsonRepresentation(BsonType.ObjectId)]
    [BsonId]
    public string Id;

    [BsonElement("active")]
    public bool Active;

    [BsonElement("user_id")]
    [BsonRepresentation(BsonType.ObjectId)]
    public string UserId;
}

class UserLookup
{
    public int EntityCount;

    public User[] UsersData;

    [BsonRepresentation(BsonType.ObjectId)]
    public string Id;
}

class UserResult
{
    public string UserId;

    public int EntityCount;

    public User UserData;
}

这样操作>

IAsyncCursor<UserResult> result = entityCol.Aggregate()
    .Match(e => e.Active)
    .Group(e => e.UserId, g => new UserResult
    {
        UserId = g.Key,
        EntityCount = g.Count(),
    })
    .Lookup(usersCol,
        lf => lf.UserId,  // localField. UserResult.UserId
        ff => ff.Id,      // foreignField. User.Id
        (UserLookup r) => r.UsersData  // result. UserLookup.UsersData
    )
    .Project(p => new UserResult
    {
        UserId = p.UsersData.First().Id,
        EntityCount = p.EntityCount,
        UserData = p.UsersData.First()
    })
    .ToCursor();

【讨论】:

  • 我不需要也需要加entityIds?我稍后会测试,这里已经很晚了。我用 mongo shell 测试更新了我的问题。
  • @Kiritonito 重新处理了查询。不需要entityIds 它不属于它。
【解决方案2】:

我认为问题在于这个

IAsyncCursor<UserResult> result = entity.Aggregate()

它应该引用你创建的实体集合,entityCol。

IAsyncCursor<UserResult> result = entityCol.Aggregate()

【讨论】:

  • 对不起,我的错误。我已经在我的真实代码中使用来自entityCol 的聚合(我从头开始编写代码,以使其清晰明了)。我会更新代码。
猜你喜欢
  • 1970-01-01
  • 2017-09-03
  • 1970-01-01
  • 2012-05-10
  • 2017-10-24
  • 1970-01-01
  • 1970-01-01
  • 2019-06-18
相关资源
最近更新 更多