MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Come raggruppare per campi diversi

Quello è stata dura!

Innanzitutto, la soluzione nuda:

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}}
])

Produzione:

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

E ora, per alcune spiegazioni:

Uno dei problemi principali qui è che ogni documento in arrivo deve far parte di due somme diverse. L'ho risolto aggiungendo un array letterale ["age", "childs"] ai tuoi documenti e quindi svolgerli da quell'array. In questo modo, ogni documento verrà presentato due volte nella fase successiva.

Fatto ciò, per facilitare l'elaborazione, cambio la rappresentazione dei dati in qualcosa di molto più gestibile come { data: "age", value: "40" }

I passaggi seguenti eseguiranno l'aggregazione dei dati di per sé. Fino al terzo $project passaggio che mapperà i campi del valore alla age corrispondente o childs campo.

Gli ultimi due passaggi racchiuderanno semplicemente i due documenti in uno, rimuovendo il _id non necessario campo.

Pffff!