sql >> Databáze >  >> NoSQL >> MongoDB

počítejte výskyty polí ve všech dokumentech pomocí mongo

Osobně nejsem velkým fanouškem transformace "dat" jako názvů klíčů ve výsledku. Principy agregačního rámce mají tendenci se shodovat, protože tento druh operací také není podporován.

Osobní preference je tedy udržovat „data“ jako „data“ a akceptovat, že zpracovaný výstup je ve skutečnosti lepší a logičtější vzhledem ke konzistentnímu návrhu objektu:

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

Což vede k tomuto výsledku:

[
    {
            "_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
            }
        ]
    }
]

Tedy počáteční $group provádí počet za "pohlaví" a shromažďuje koníčky do řady polí. Poté vás de-normalizuje $unwind dvakrát, abyste získali jednotlivé položky, $group získat součty za koníčka pro každé pohlaví a nakonec přeskupit pole pro každé pohlaví samostatně.

Jsou to stejná data, mají konzistentní a organickou strukturu, která se snadno zpracovává, a MongoDB a agregační rámec byly při vytváření tohoto výstupu docela spokojené.

Pokud opravdu musíte svá data převést na názvy klíčů (a stále vám to doporučuji ne, protože to není dobrý vzor, ​​který byste měli při návrhu následovat), pak je provedení takové transformace z konečného stavu pro zpracování klientského kódu poměrně triviální. Jako základní příklad JavaScriptu vhodný pro shell:

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

A pak v podstatě zpracováváte každý výsledek kurzoru do požadovaného výstupního formuláře, což ve skutečnosti není agregační funkce, která je na serveru skutečně vyžadována:

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

Tam, kde by to mělo být také docela triviální, implementovat tento druh manipulace do proudového zpracování výsledku kurzoru, aby se transformoval podle potřeby, protože je to v podstatě stejná logika.

Na druhou stranu můžete vždy implementovat veškerou manipulaci na serveru pomocí mapReduce:

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

MapReduce má svůj vlastní odlišný styl výstupu, ale stejné principy se používají při akumulaci a manipulaci, i když pravděpodobně nejsou tak účinné, jak to dokáže agregační rámec:

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

Na konci dne stále říkám, že první forma zpracování je nejúčinnější a poskytuje podle mého názoru nejpřirozenější a nejkonzistentnější práci výstupu dat, aniž bych se pokoušel převádět datové body na názvy klíčů. Pravděpodobně bude nejlepší zvážit následování tohoto vzoru, ale pokud opravdu musíte, pak existují způsoby, jak zpracovat výsledky do požadované podoby v různých přístupech ke zpracování.




  1. Odhalení Redis pomocí Ingress Nginx Controller

  2. Existuje nějaké omezení počtu argumentů, které zvládnou příkazy redis, jako je ZADD nebo HMGET?

  3. Jak zjistím čas, kdy je můj klíč uložen v Redis Cache/db?

  4. Jak automaticky zapojit RedisTemplate<String,Long>