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

tæl array-forekomster på tværs af alle dokumenter med mongo

Personligt er jeg ikke en stor fan af at transformere "data" som navne på nøgler i et resultat. Aggregeringsrammeprincipperne har tendens til at stemme overens, da denne form for operation heller ikke understøttes.

Så den personlige præference er at bevare "data" som "data" og acceptere, at det behandlede output faktisk er bedre og mere logisk i forhold til et konsistent objektdesign:

db.people.aggregate([
    { "$group": {
        "_id": "$sex",
        "hobbies": { "$push": "$hobbies" },
        "total": { "$sum": 1 }
    }},
    { "$unwind": "$hobbies" },
    { "$unwind": "$hobbies" },
    { "$group": {
        "_id": {
            "sex": "$_id",
            "hobby": "$hobbies"
        },
        "total": { "$first": "$total" },
        "hobbyCount": { "$sum": 1 }
    }},
    { "$group": {
        "_id": "$_id.sex",
        "total": { "$first": "$total" },
        "hobbies": {
            "$push": { "name": "$_id.hobby", "count": "$hobbyCount" }
        }
    }}
])
 

Hvilket giver et resultat som dette:

[ { "_id" : "female", "total" : 1, "hobbies" : [ { "name" : "tennis", "count" : 1 }, { "name" : "football", "count" : 1 } ] }, { "_id" : "male", "total" : 2, "hobbies" : [ { "name" : "swimming", "count" : 1 }, { "name" : "tennis", "count" : 2 }, { "name" : "football", "count" : 2 } ] } ]

Så den indledende $group tæller pr. "køn" og samler hobbyerne i en række rækker. Så for at denormalisere dig $unwind to gange for at få enestående elementer, $group for at få totalerne pr. hobby under hvert køn og til sidst omgruppere en række for hvert køn alene.

Det er de samme data, det har en konsistent og organisk struktur, der er nem at behandle, og MongoDB og aggregeringsrammen var ret glade for at producere dette output.

Hvis du virkelig skal konvertere dine data til navne på nøgler (og jeg anbefaler stadig, at du ikke gør det, da det ikke er et godt mønster at følge i design), så er det ret trivielt at udføre en sådan transformation fra den endelige tilstand for klientkodebehandling. Som et grundlæggende JavaScript-eksempel, der passer til skallen:

var out = db.people.aggregate([
    { "$group": {
        "_id": "$sex",
        "hobbies": { "$push": "$hobbies" },
        "total": { "$sum": 1 }
    }},
    { "$unwind": "$hobbies" },
    { "$unwind": "$hobbies" },
    { "$group": {
        "_id": {
            "sex": "$_id",
            "hobby": "$hobbies"
        },
        "total": { "$first": "$total" },
        "hobbyCount": { "$sum": 1 }
    }},
    { "$group": {
        "_id": "$_id.sex",
        "total": { "$first": "$total" },
        "hobbies": {
            "$push": { "name": "$_id.hobby", "count": "$hobbyCount" }
        }
    }}
]).toArray();

out.forEach(function(doc) {
    var obj = {};
    doc.hobbies.sort(function(a,b) { return a.count < b.count });
    doc.hobbies.forEach(function(hobby) {
        obj[hobby.name] = hobby.count;
    });
    doc.hobbies = obj;
    printjson(doc);
});
 

Og så behandler du dybest set hvert markørresultat til den ønskede outputform, hvilket i virkeligheden ikke er en aggregeringsfunktion, der virkelig er påkrævet på serveren alligevel:

{
    "_id" : "female",
    "total" : 1,
    "hobbies" : {
        "tennis" : 1,
        "football" : 1
    }
}
{
    "_id" : "male",
    "total" : 2,
    "hobbies" : {
        "tennis" : 2,
        "football" : 2,
        "swimming" : 1
    }
}
 

Hvor det også burde være ret trivielt at implementere den slags manipulation i strømbehandling af markørresultatet for at transformere efter behov, da det dybest set bare er den samme logik.

På den anden side kan du altid implementere al manipulation på serveren ved at bruge mapReduce i stedet for:

db.people.mapReduce(
    function() {
        emit(
            this.sex,
            { 
                "total": 1,
                "hobbies": this.hobbies.map(function(key) {
                    return { "name": key, "count": 1 };
                })
            }
        );
    },
    function(key,values) {
        var obj  = {},
            reduced = {
                "total": 0,
                "hobbies": []
            };

        values.forEach(function(value) {
            reduced.total += value.total;
            value.hobbies.forEach(function(hobby) {
                if ( !obj.hasOwnProperty(hobby.name) )
                    obj[hobby.name] = 0;
                obj[hobby.name] += hobby.count;
            });
        });

        reduced.hobbies = Object.keys(obj).map(function(key) {
            return { "name": key, "count": obj[key] };
        }).sort(function(a,b) {
            return a.count < b.count;
        });

        return reduced;
    },
    { 
        "out": { "inline": 1 },
        "finalize": function(key,value) {
            var obj = {};
            value.hobbies.forEach(function(hobby) {
                obj[hobby.name] = hobby.count;
            });
            value.hobbies = obj;
            return value;
        }
    }
)
 

Hvor mapReduce har sin egen distinkte output-stil, men de samme principper bruges i akkumulering og manipulation, hvis det ikke er sandsynligt så effektivt som aggregeringsrammen kan gøre:

   "results" : [
        {
            "_id" : "female",
            "value" : {
                "total" : 1,
                "hobbies" : {
                    "football" : 1,
                    "tennis" : 1
                }
            }
        },
        {
            "_id" : "male",
            "value" : {
                "total" : 2,
                "hobbies" : {
                    "football" : 2,
                    "tennis" : 2,
                    "swimming" : 1
                }
            }
        }
    ]
 

I slutningen af ​​dagen siger jeg stadig, at den første form for behandling er den mest effektive og giver efter min mening den mest naturlige og konsekvente bearbejdning af dataoutputtet, uden selv at forsøge at konvertere datapunkterne til navne på nøgler. Det er nok bedst at overveje at følge det mønster, men hvis du virkelig skal, så er der måder at manipulere resultater til en ønsket form i forskellige tilgange til behandling.




  1. MongoDB:Fatal fejl:Klasse 'MongoClient' blev ikke fundet

  2. Optimering af dit Linux-miljø til MongoDB

  3. Vælg strenglængde i mongodb

  4. Redis koncept:I hukommelsen eller DB?