Я пытаюсь использовать агрегатный конвейер с текущими интерфейсами, но безуспешно, хотя я не получаю никакой ошибки (все поля из результата равны нулю).
У меня есть Пользователь класс:
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 и UserId
и UserData
равны нулю. По сути, я хочу:
- Получить все активные сущности (
Match
). - Сгруппировать их по идентификатору пользователя (
Group
). - Поиск в коллекции пользователей для получения пользовательских данных (
Lookup
). - Проецирование результата для возврата простого объекта с числом объектов и пользовательскими данными (
Project
).
----- Обновление 1
Хорошо, после игры с оболочкой mon go, я думаю, что нашел проблему. Кажется, mon go не может найти записи по id с ObjectId
, только со строками. Это странно, я нашел этот ответ , и, кажется, его можно найти с помощью ObjectId (по крайней мере, в прошлом).
В оболочке mon go, если я использую db.users.find({ _id: ObjectId("...") })
он ничего не возвращает, но с db.users.find({ _id: "..." })
возвращает ожидаемого пользователя.
Я написал этот агрегатный запрос с нуля для запуска на оболочке, вот он:
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": "$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
устанавливается на 0 на этапе $ lookup .
Я переименовал поле UserId
из UserResult
в Id
и добавил атрибут [BsonElement("_id")]
.
Теперь я получаю в результате и идентификатор пользователя, и количество объектов, но UserData
по-прежнему равен нулю.