Монгодский агрегат (счет) на нескольких полях одновременно

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

{
    "_id" : "someuniqueeventid",
    "event" : "event_type_1",
    "date" : ISODate("2014-01-14T00:00:00Z"),
}

Я хочу группировать "event" и подсчитывать, сколько из каждого типа события произошло в каждый день недели. В принципе, я хочу получить что-то вроде:

{
    "_id": "event_type_1",
    "1": "number of event_type_1 for Monday",
    "2": "number of event_type_1 for Tuesday",
    ...
},
{
    "_id": "event_type_2",
    ...
}

К сожалению, я застрял в:

db.data.aggregate([ {$project: {date_of_week: {$dayOfWeek: "$date"}, event: "$event"}}, 
                    {$group: {_id: "$event", .... } ])

Любые идеи?

Ответ 1

Структура агрегации не будет создавать ключи на основе данных, и вы даже не должны этого делать, поскольку "данные" - это не ключ, а фактически данные, поэтому вам следует придерживаться шаблона.

Это означает, что вы можете просто сделать это:

db.data.aggregate([
    { "$group": {
        "_id": {
            "event_type": "$event",
            "day": { "$dayOfWeek": "$date" }
        },
        "count": { "$sum": 1 } 
    }}
])

И это будет подсчитывать вхождения в день недели на событие, хотя в нескольких документах в выводе, но это легко изменить на один документ на событие:

db.data.aggregate([
    { "$group": {
        "_id": {
            "event_type": "$event",
            "day": { "$dayOfWeek": "$date" }
        },
        "count": { "$sum": 1 } 
    }},
    { "$group": {
        "_id": "$_id.event_type",
        "days": { "$push": { "day": "$_id.day", "count": "$count" } }
    }}
])

И это в виде массива, но он по-прежнему содержит результаты, которые вы хотите.

Если вы действительно хотите выполнить свою точную форму, то вы хотите сделать что-то вроде этого:

db.data.aggregate([
    { "$group": {
        "_id": "$event",
        "1": {
            "$sum": {
                "$cond": [
                    { "$eq": [{ "$dayOfWeek": "$date" }, 1 ] },
                    1,
                    0
                ]
            }
        },
        "2": {
            "$sum": {
                "$cond": [
                    { "$eq": [{ "$dayOfWeek": "$date" }, 2 ] },
                    1,
                    0
                ]
            }
        },
        "3": {
            "$sum": {
                "$cond": [
                    { "$eq": [{ "$dayOfWeek": "$date" }, 3 ] },
                    1,
                    0
                ]
            }
        },
        "4": {
            "$sum": {
                "$cond": [
                    { "$eq": [{ "$dayOfWeek": "$date" }, 4 ] },
                    1,
                    0
                ]
            }
        },
        "5": {
            "$sum": {
                "$cond": [
                    { "$eq": [{ "$dayOfWeek": "$date" }, 5 ] },
                    1,
                    0
                ]
            }
        },
        "6": {
            "$sum": {
                "$cond": [
                    { "$eq": [{ "$dayOfWeek": "$date" }, 6 ] },
                    1,
                    0
                ]
            }
        },
        "7": {
            "$sum": {
                "$cond": [
                    { "$eq": [{ "$dayOfWeek": "$date" }, 7 ] },
                    1,
                    0
                ]
            }
        }
    }}
)

Но это действительно затянуто, так что ИМХО, я бы остановился на первом или, может быть, втором решении, так как они короче и легче читаются.

Ответ 2

С MongoDb 3.4.4 и новее вы можете использовать оператор $arrayToObject, чтобы получить подсчеты. Вам потребуется запустить следующий агрегатный конвейер:

db.data.aggregate([
    { 
        "$group": {
            "_id": {
                "event": "$event",
                "day": { "$substr": [ { "$dayOfWeek": "$date" }, 0, -1 ] }
            },
            "count": { "$sum": 1 }
        }
    },
    { 
        "$group": {
            "_id": "$_id.event",
            "counts": {
                "$push": {
                    "k": "$_id.day",
                    "v": "$count"
                }
            }
        }
    },
    { 
        "$project": {
            "counts": { "$arrayToObject": "$counts" }
        } 
    }    
])