Несколько вложенных групп в массиве

У меня есть группа элементов в MongoDB, как указано ниже:

/* 1 */
{
    "_id" : ObjectId("58736c7f7d43c305461cdb9b"),
    "Name" : "Kevin",
    "pb_event" : [ 
        {
            "event_type" : "Birthday",
            "event_date" : "2014-08-31"
        }, 
        {
            "event_type" : "Anniversary",
            "event_date" : "2014-08-31"
        }
    ]
}

/* 2 */
{
    "_id" : ObjectId("58736cfc7d43c305461cdba8"),
    "Name" : "Peter",
    "pb_event" : [ 
        {
            "event_type" : "Birthday",
            "event_date" : "2014-08-31"
        }, 
        {
            "event_type" : "Anniversary",
            "event_date" : "2015-03-24"
        }
    ]
}

/* 3 */
{
    "_id" : ObjectId("58736cfc7d43c305461cdba9"),
    "Name" : "Pole",
    "pb_event" : [ 
        {
            "event_type" : "Birthday",
            "event_date" : "2015-03-24"
        }, 
        {
            "event_type" : "Work Anniversary",
            "event_date" : "2015-03-24"
        }
    ]
}

Теперь я хочу получить результат с группой event_date затем после группы на event_type, event_type содержат все имена связанных пользователей, затем количество записей в соответствующем массиве.

Ожидаемый результат

/* 1 */
{    
    "event_date" : "2014-08-31",
    "data" : [ 
        {
            "event_type" : "Birthday",
            "details" : [ 
                {
                    "_id" : ObjectId("58736c7f7d43c305461cdb9b"),
                    "name" : "Kevin"
                }, 
                {
                    "_id" : ObjectId("58736cfc7d43c305461cdba8"),
                    "name" : "Peter"
                }
            ],
            "count" : 2
        }, 
        {
            "event_type" : "Anniversary",
            "details" : [ 
                {
                    "_id" : ObjectId("58736c7f7d43c305461cdb9b"),
                    "name" : "Kevin"
                }
            ],
            "count" : 1
        }
    ]
}

/* 2 */
{
    "event_date" : "2015-03-24",
    "data" : [ 
        {
            "event_type" : "Anniversary",
            "details" : [ 
                {
                    "_id" : ObjectId("58736cfc7d43c305461cdba8"),
                    "name" : "Peter"
                }
            ],
            "count" : 1
        }, 
        {
            "event_type" : "Birthday",
            "details" : [ 
                {
                    "_id" : ObjectId("58736cfc7d43c305461cdba9"),
                    "name" : "Pole"
                }
            ],
            "count" : 1
        }, 
        {
            "event_type" : "Work Anniversary",
            "details" : [ 
                {
                    "_id" : ObjectId("58736cfc7d43c305461cdba9"),
                    "name" : "Pole"
                }
            ],
            "count" : 1
        }
    ]
}

1 ответ

Решение

При использовании структуры агрегации вам потребуется запустить конвейер, который имеет следующие этапы, чтобы получить желаемый результат:

db.collection.aggregate([
    { "$unwind": "$pb_event" },
    {
        "$group": {
            "_id": {
                "event_date": "$pb_event.event_date",
                "event_type": "$pb_event.event_type" 
            },            
            "details": {
                "$push": {
                    "_id": "$_id",
                    "name": "$Name"
                }
            },
            "count": { "$sum": 1 }            
        }
    },    
    {
        "$group": {
            "_id": "$_id.event_date",            
            "data": {
                "$push": {
                    "event_type": "$_id.event_type",
                    "details": "$details",
                    "count": "$count"
                }
            }           
        }
    },
    {
        "$project": {
            "_id": 0,
            "event_date": "$_id",
            "data": 1
        }
    }
])

В приведенном выше конвейере первым шагом является $unwind оператор

{ "$unwind": "$pb_event" }

что очень удобно, когда данные хранятся в виде массива. Когда оператор разматывания применяется к полю данных списка, он создает новую запись для каждого элемента поля данных списка, к которому применяется размотка. Это в основном сглаживает данные.

Это необходимая операция для следующего этапа конвейера, $group шаг, на котором вы группируете сплющенные документы по деконструированным pb_event поля массива event_date а также event_type:

{
    "$group": {
        "_id": {
            "event_date": "$pb_event.event_date",
            "event_type": "$pb_event.event_type" 
        },            
        "details": {
            "$push": {
                "_id": "$_id",
                "name": "$Name"
            }
        },
        "count": { "$sum": 1 }            
    }
},

$group Оператор конвейера похож на SQL GROUP BY пункт. В SQL вы не можете использовать GROUP BY если вы не используете какую-либо функцию агрегирования. Точно так же вы должны использовать функцию агрегирования в MongoDB (называемую оператором накопителя). Вы можете прочитать больше о функциях агрегирования здесь.

В этом $group операция, логика для расчета совокупного количества, т. е. общее количество документов в группе, используя $sum оператор аккумулятора. В пределах одного и того же конвейера вы можете агрегировать список name а также _id субдокументы с использованием $push оператор, который возвращает массив значений выражений для каждой группы.

Предыдущий $group трубопровод

{
    "$group": {
        "_id": "$_id.event_date",            
        "data": {
            "$push": {
                "event_type": "$_id.event_type",
                "details": "$details",
                "count": "$count"
            }
        }           
    }
}

будет дополнительно агрегировать результаты последнего конвейера, группируя по event_date, которая формирует основу для желаемого вывода путем создания нового списка данных с использованием $push а потом финал $project этап трубопровода

{
    "$project": {
        "_id": 0,
        "event_date": "$_id",
        "data": 1
    }
}

изменяет поля документов путем переименования _id поле для event_date и сохраняя другое поле.

Другие вопросы по тегам