Конвейер MongoDB $group (в Mongoose)

У меня есть коллекция Mongo, содержащая документы, которые выглядят так:

{
    "record" : "1",
    "site_id" : "abc1",
    "month" : "2013-12",
    "recorded" : ISODate("2013-12-18T05:00:00.000Z"),
    "status" : "OK",
    "comment" : "blah blah blah..."
}

{
    "record" : "2",
    "site_id" : "abc1",
    "month" : "2013-12",
    "recorded" : ISODate("2013-12-18T06:00:00.000Z"),
    "status" : "OK",
    "comment" : "blah blah blah..."
}

{
    "record" : "3",
    "site_id" : "abc2",
    "month" : "2013-12",
    "recorded" : ISODate("2013-12-18T06:00:00.000Z"),
    "status" : "OK",
    "comment" : "blah blah blah..."
}

{
    "record" : "4",
    "site_id" : "abc2",
    "month" : "2013-12",
    "recorded" : ISODate("2013-12-18T11:00:00.000Z"),
    "status" : "ERROR",
    "comment" : "something wrong"
}

{
    "record" : "5",
    "site_id" : "abc2",
    "month" : "2013-11",
    "recorded" : ISODate("2013-11-17T08:00:00.000Z"),
    "status" : "OK",
    "comment" : "blah blah blah..."
}

Я хочу использовать оператор $group, чтобы сделать вызов Mongoose для возврата всех значений последней записи (определяемой ISODate()) за данный месяц для каждого site_id. Итак, за месяц 2013-12 я хотел бы получить запись 2 (последнюю в 2013-12 для abc1) и запись 4 (последнюю в 2013-12 для abc2).

Я могу сделать это с помощью функции map/reduce, но мне было интересно, выполнимо ли это только с API Mongoose и структурой агрегации (без вызова функции map/reduce). Я чувствую, что это должно быть, но я не смог заставить это работать!


person user3115148    schedule 18.12.2013    source источник
comment
Не могли бы вы отредактировать вопрос, включив в него некоторые попытки, которые вы предприняли, и то, что не сработало? С платформой агрегации и конвейером лучше работать шаг за шагом.   -  person WiredPrairie    schedule 18.12.2013


Ответы (2)


Предполагая, что все ваши документы имеют одинаковые поля, вы можете использовать Aggregation Framework, чтобы найти «последнюю» запись для каждой группы месяца и сайта.

ПРИМЕЧАНИЕ. Хотя приведенный ниже запрос возвращает ожидаемые результаты, он не слишком эффективен или масштабируем, поэтому вам следует протестировать репрезентативный набор данных. Подход $group требует сортировки на первом этапе гораздо большего количества данных, чем вы на самом деле хотите вернуть. Альтернативным подходом может быть запуск агрегации для каждого сайта с использованием $sort и $limit. MongoDB 2.4+ имеет оптимизацию для варианта использования Aggregation Framework, когда $sort следует за $limit (поскольку известно количество первых n результатов, которые необходимо сохранить), поэтому несколько эффективных запросов могут иметь меньшее общее время выполнения, чем один неэффективный.

Образец агрегации:

db.sites.aggregate(

    // Need to sort first so "last" makes sense in the $group
    { $sort: {
        month : 1,
        site_id: 1,
        recorded: 1,
    }},

    // Find the last monthly record for each site_id
    { $group: {
        _id: { "month" : "$month", site_id: "$site_id" },
        record: { $last: "$record" },
        site_id: { $last: "$site_id" },
        month: { $last: "$month" },
        recorded: { $last: "$recorded" },
        status: { $last: "$status" },
        comment: { $last: "$comment" }
    }}

)

Пример результата:

{
    "result" : [
        {
            "_id" : {
                "month" : "2013-12",
                "site_id" : "abc2"
            },
            "record" : "4",
            "site_id" : "abc2",
            "month" : "2013-12",
            "recorded" : ISODate("2013-12-18T11:00:00Z"),
            "status" : "ERROR",
            "comment" : "something wrong"
        },
        {
            "_id" : {
                "month" : "2013-12",
                "site_id" : "abc1"
            },
            "record" : "2",
            "site_id" : "abc1",
            "month" : "2013-12",
            "recorded" : ISODate("2013-12-18T06:00:00Z"),
            "status" : "OK",
            "comment" : "blah blah blah..."
        }
    ],
    "ok" : 1
}
person Stennie    schedule 18.12.2013

При использовании aggregate для получения определенного элемента каждой группы вам необходимо включить этап $sort в свой конвейер перед этапом $group, который ставит желаемый элемент первым для каждой группы, чтобы вы могли использовать оператор $first для выбора нужных полей. первый документ в этой группе.

В оболочке:

db.test.aggregate(
    {$match: {month: '2013-12'}}, 
    {$sort: {recorded: -1}}, 
    {$group: {
        _id: '$site_id', 
        record: {$first: '$record'},
        recorded: {$first: '$recorded'},
        status: {$first: '$status'},
        comment: {$first: '$comment'}
    }})
person JohnnyHK    schedule 18.12.2013