sql >> Base de Datos >  >> NoSQL >> MongoDB

Cómo agrupar por diferentes campos

Eso ¡fue difícil!

Primero, la solución básica:

db.test.aggregate([
 { "$match": { "user": "Hans" } },
 // duplicate each document: one for "age", the other for "childs"
 { $project: { age: "$age", childs: "$childs",
               data: {$literal: ["age", "childs"]}}},
 { $unwind: "$data" },
 // pivot data to something like { data: "age", value: "40" }
 { $project: { data: "$data",
               value: {$cond: [{$eq: ["$data", "age"]},
                               "$age", 
                               "$childs"]} }},
 // Group by data type, and count
 { $group: { _id: {data: "$data", value: "$value" }, 
             count: { $sum: 1 }, 
             value: {$first: "$value"} }},
 // aggregate values in an array for each independant (type,value) pair
 { $group: { _id: "$_id.data", values: { $push: { count: "$count", value: "$value" }} }} ,
 // project value to the correctly name field
 { $project: { result: {$cond: [{$eq: ["$_id", "age"]},
                               {age: "$values" }, 
                               {childs: "$values"}]} }},
 // group all data in the result array, and remove unneeded `_id` field 
 { $group: { _id: null, result: { $push: "$result" }}},
 { $project: { _id: 0, result: 1}}
])

Produciendo:

{
    "result" : [
        {
            "age" : [
                {
                    "count" : 3,
                    "value" : "40"
                },
                {
                    "count" : 1,
                    "value" : "50"
                }
            ]
        },
        {
            "childs" : [
                {
                    "count" : 1,
                    "value" : "1"
                },
                {
                    "count" : 3,
                    "value" : "2"
                }
            ]
        }
    ]
}

Y ahora, para algunas explicaciones:

Uno de los principales problemas aquí es que cada documento entrante tiene que ser parte de dos diferentes sumas. Lo resolví agregando una matriz literal ["age", "childs"] a sus documentos, y luego desenrollarlos por esa matriz. De esa forma, cada documento se presentará dos veces en la etapa posterior.

Una vez hecho esto, para facilitar el procesamiento, cambio la representación de datos a algo mucho más manejable como { data: "age", value: "40" }

Los siguientes pasos realizarán la agregación de datos per se. Hasta el tercer $project paso que asignará los campos de valor a la correspondiente age o childs campo.

Los dos pasos finales simplemente envolverán los dos documentos en uno, eliminando el _id innecesario. campo.

¡Pfff!