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

MongoDB Výpočet hodnot ze dvou polí, řazení a omezení

Aktuální zpracování je mapReduce

Pokud to potřebujete provést na serveru a seřadit nejlepší výsledky a ponechat pouze prvních 100, můžete k tomu použít mapReduce takto:

db.test.mapReduce(
    function() {
        var input = [0.1,0.3,0.4];
        var value = Array.sum(this.vals.map(function(el,idx) {
            return Math.abs( el - input[idx] )
        }));

        emit(null,{ "output": [{ "_id": this._id, "value": value }]});
    },
    function(key,values) {
        var output = [];

        values.forEach(function(value) {
            value.output.forEach(function(item) {
                output.push(item);
            });
        });

        output.sort(function(a,b) {
            return a.value < b.value;
        });

        return { "output": output.slice(0,100) };
    },
    { "out": { "inline": 1 } }
)

Funkce mapovače tedy provádí výpočet a výstup je vše pod stejným klíčem, takže všechny výsledky jsou odeslány do reduktoru. Koncový výstup bude obsažen v poli v jediném výstupním dokumentu, takže je důležité, aby všechny výsledky byly emitovány se stejnou hodnotou klíče, a aby výstup každého výstupu byl sám pole, takže mapReduce může fungovat správně.

Třídění a redukce se provádí v samotném reduktoru, protože každý vysílaný dokument je kontrolován a prvky jsou umístěny do jednoho dočasného pole, tříděny a jsou vráceny nejlepší výsledky.

To je důležité a právě to je důvod, proč to emitor vytváří jako pole, i když zpočátku jde o jediný prvek. MapReduce funguje tak, že výsledky zpracovává po „kusech“, takže i když mají všechny vydávané dokumenty stejný klíč, nejsou všechny zpracovány najednou. Reduktor spíše umístí své výsledky zpět do fronty vysílaných výsledků, aby byly redukovány, dokud nezůstane pouze jeden dokument pro daný klíč.

Omezuji zde výstup "slice" na 10 kvůli stručnosti výpisu a včetně statistik, aby bylo jasné, jak je vidět 100 cyklů snížení vyvolaných na tomto 10000 vzorku:

{
    "results" : [
        {
            "_id" : null,
            "value" : {
                "output" : [
                    {
                        "_id" : ObjectId("56558d93138303848b496cd4"),
                        "value" : 2.2
                    },
                    {
                        "_id" : ObjectId("56558d96138303848b49906e"),
                        "value" : 2.2
                    },
                    {
                        "_id" : ObjectId("56558d93138303848b496d9a"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d93138303848b496ef2"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d94138303848b497861"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d94138303848b497b58"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d94138303848b497ba5"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d94138303848b497c43"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d95138303848b49842b"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d96138303848b498db4"),
                        "value" : 2.1
                    }
                ]
            }
        }
    ],
    "timeMillis" : 1758,
    "counts" : {
            "input" : 10000,
            "emit" : 10000,
            "reduce" : 100,
            "output" : 1
    },
    "ok" : 1
}

Jedná se tedy o výstup jednoho dokumentu ve specifickém formátu mapReduce, kde „hodnota“ obsahuje prvek, který je polem setříděných a omezených výsledků.

Budoucí zpracování je agregované

V době psaní je aktuální nejnovější stabilní verze MongoDB 3.0 a postrádá funkcionalitu, která by umožnila vaši operaci. Ale nadcházející vydání 3.2 zavádí nové operátory, které to umožňují:

db.test.aggregate([
    { "$unwind": { "path": "$vals", "includeArrayIndex": "index" }},
    { "$group": {
        "_id": "$_id",
        "result": {
            "$sum": {
                "$abs": {
                    "$subtract": [ 
                        "$vals", 
                        { "$arrayElemAt": [ { "$literal": [0.1,0.3,0.4] }, "$index" ] } 
                    ]
                }
            }
        }
    }},
    { "$sort": { "result": -1 } },
    { "$limit": 100 }
])

Také omezením na stejných 10 výsledků pro stručnost získáte výstup takto:

{ "_id" : ObjectId("56558d96138303848b49906e"), "result" : 2.2 }
{ "_id" : ObjectId("56558d93138303848b496cd4"), "result" : 2.2 }
{ "_id" : ObjectId("56558d96138303848b498e31"), "result" : 2.1 }
{ "_id" : ObjectId("56558d94138303848b497c43"), "result" : 2.1 }
{ "_id" : ObjectId("56558d94138303848b497861"), "result" : 2.1 }
{ "_id" : ObjectId("56558d96138303848b499037"), "result" : 2.1 }
{ "_id" : ObjectId("56558d96138303848b498db4"), "result" : 2.1 }
{ "_id" : ObjectId("56558d93138303848b496ef2"), "result" : 2.1 }
{ "_id" : ObjectId("56558d93138303848b496d9a"), "result" : 2.1 }
{ "_id" : ObjectId("56558d96138303848b499182"), "result" : 2.1 }

To je možné z velké části díky $unwind byl upraven tak, aby ve výsledcích promítal pole, které obsahuje index pole, a také kvůli $arrayElemAt což je nový operátor, který dokáže extrahovat prvek pole jako singulární hodnotu z poskytnutého indexu.

To umožňuje „vyhledat“ hodnoty podle pozice indexu z vašeho vstupního pole, aby bylo možné aplikovat matematiku na každý prvek. Vstupní pole usnadňuje stávající $literal operátor tak $arrayElemAt nestěžuje si a znovu jej rozpoznává jako pole (v současnosti se zdá být malou chybou, protože jiné funkce pole nemají problém s přímým vstupem) a získá odpovídající hodnotu indexu pomocí pole „index“ vytvořeného $unwind pro srovnání.

Výpočet provádí $subtract a samozřejmě další nový operátor v $abs aby vyhovoval vaší funkčnosti. Také protože bylo nejprve nutné pole rozvinout, vše se to děje uvnitř $group fáze shromažďující všechny členy pole na dokument a použití přidání položek prostřednictvím $sum akumulátor.

Nakonec jsou všechny výsledkové dokumenty zpracovány pomocí $sort a poté $limit se použije pouze pro návrat nejlepších výsledků.

Shrnutí

I s novou funkcionalitou, která bude k dispozici pro agregační rámec pro MongoDB, je diskutabilní, který přístup je ve skutečnosti efektivnější pro výsledky. To je z velké části způsobeno tím, že stále existuje potřeba $unwind obsah pole, který efektivně vytváří kopii každého dokumentu na člena pole v kanálu, který má být zpracován, a to obecně způsobuje režii.

Takže i když je mapReduce jediným současným způsobem, jak toho dosáhnout až do nového vydání, může ve skutečnosti překonat agregační příkaz v závislosti na množství dat, která mají být zpracována, a to navzdory skutečnosti, že agregační rámec funguje na nativních kódovaných operátorech spíše než na přeloženém JavaScriptu. operace.

Jako u všech věcí se vždy doporučuje testování, abyste zjistili, které pouzdro lépe vyhovuje vašim účelům a které poskytuje nejlepší výkon pro vaše očekávané zpracování.

Ukázka

Samozřejmě očekávaný výsledek pro ukázkový dokument uvedený v otázce je 0.9 podle použité matematiky. Ale jen pro mé testovací účely je zde krátký seznam použitý ke generování některých ukázkových dat, která jsem chtěl alespoň ověřit, že kód mapReduce funguje tak, jak má:

var bulk = db.test.initializeUnorderedBulkOp();

var x = 10000;

while ( x-- ) {
    var vals = [0,0,0];

    vals = vals.map(function(val) {
        return Math.round((Math.random()*10),1)/10;
    });

    bulk.insert({ "vals": vals });

    if ( x % 1000 == 0) {
        bulk.execute();
        bulk = db.test.initializeUnorderedBulkOp();
    }
}

Pole jsou zcela náhodné hodnoty s jednou desetinnou čárkou, takže v uvedených výsledcích, které jsem uvedl jako ukázkový výstup, není mnoho rozdělení.




  1. Jak mohu změnit/definovat výchozí databázi Mongodb v Spring-data?

  2. sledovat smazané dokumenty v omezené sbírce Mongo DB

  3. nenalezl hostitele odpovídající předvolbám čtení v mongodb

  4. Použijte createView s allowDiskUse