MongoDB C# Свободный агрегат - PullRequest
2 голосов
/ 04 мая 2020

Я пытаюсь использовать агрегатный конвейер с текущими интерфейсами, но безуспешно, хотя я не получаю никакой ошибки (все поля из результата равны нулю).

У меня есть Пользователь класс:

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 равны нулю. По сути, я хочу:

  1. Получить все активные сущности (Match).
  2. Сгруппировать их по идентификатору пользователя (Group).
  3. Поиск в коллекции пользователей для получения пользовательских данных (Lookup).
  4. Проецирование результата для возврата простого объекта с числом объектов и пользовательскими данными (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 по-прежнему равен нулю.

Ответы [ 2 ]

0 голосов
/ 05 мая 2020

В данный момент я пытаюсь использовать свободный подход, но сейчас форма запроса работает>

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 - преобразование строк не работает в группировке, является правильным.

это работает. То есть вы должны изменить типы идентификаторов на ObjectId. Например>

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

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

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

Таким образом, это работает>

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();
0 голосов
/ 05 мая 2020

Я думаю, что проблема заключается в этом

IAsyncCursor<UserResult> result = entity.Aggregate()

Это должно относиться к коллекции созданных вами сущностей entityCol.

IAsyncCursor<UserResult> result = entityCol.Aggregate()
...