c# - MongoDB C# Fluent 聚合
问题描述
我正在尝试使用具有流畅接口的聚合管道但没有成功,尽管我没有收到任何错误(结果中的所有字段均为空)。
我有这个用户类:
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;
}
用户查找类。这用于$lookup
.
class UserLookup
{
public int EntityCount;
public IEnumerable<User> UsersData;
}
用户结果类。这用于组和投影。
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 并且两者UserId
都UserData
为空。基本上,我想要的是:
- 获取所有处于活动状态的实体 (
Match
)。 - 按用户 ID (
Group
) 对它们进行分组。 - 在 users 集合中查找以获取用户数据 (
Lookup
)。 - 投影结果以返回具有实体计数和用户数据 (
Project
) 的简单对象。
----- 更新 1
好的,在玩了mongo shell之后,我想我发现了问题。似乎 mongo 找不到带有 id 的条目ObjectId
,只能找到字符串。这很奇怪,我找到了这个答案,似乎可以使用 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
它被忽略的字段(这解释了为什么它在结果中总是为空)。此外,您可以注意到在$lookup阶段_id
被设置为 0 。
我将字段UserId
从UserResult
to重命名Id
并添加了属性[BsonElement("_id")]
。
现在,我在结果中同时获得了用户 ID 和实体计数,但UserData
仍然为空。
解决方案
查询表格,有效>
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();
推荐阅读
- python - 如何根据范围过滤数字?
- r - ggplot() 有错误:需要 TRUE/FALSE 的地方缺少值
- r - knitr:当我编译我的 .Rnw 文件时,为什么“环境”面板中什么也没有出现
- c# - 是否可以从 c# 中的自定义类中删除属性
- mysql - 具有多个帐户的应用程序的数据库结构应该是什么,每个帐户具有相似类型的数据?
- java - 无法识别错误推理 - 与 ArrayList 和 FOR 循环相关,我该如何修复我的代码?
- c++ - 在 qwebview qt 上使用 qwebelement 进行虚假点击
- mysql - Mysql 上的 Gtfs 数据库 - 搜索区域内的路线时查询缓慢
- php - 如何将变量与 Curl 代码 Php 连接起来
- file - 批处理文件将子文件夹内容从映射位置移动到服务器