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

Součet vnořených dokumentů v Mongoose

Pomocí aggregate() můžete spustit následující kanál, který používá $sum operátor k získání požadovaných výsledků:

const results = await Cart.aggregate([
    { "$addFields": {
        "totalPrice": {
            "$sum": "$products.subTotal"
        }
    } },
]);

console.log(JSON.stringify(results, null, 4));

a následuje odpovídající operace aktualizace:

db.carts.updateMany(
   { },
   [
        { "$set": {
            "totalPrice": {
                "$sum": "$products.subTotal"
            }
        } },
    ]
)

Nebo pokud používáte MongoDB 3.2 a starší verze, kde $sum je k dispozici pouze ve fázi $group, můžete to udělat

const pipeline = [
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$_id",
            "products": { "$push": "$products" },
            "userPurchased": { "$first": "$userPurchased" },
            "totalPrice": { "$sum": "$products.subTotal" }
        }
    }
]

Cart.aggregate(pipeline)
    .exec(function(err, results){
        if (err) throw err;
        console.log(JSON.stringify(results, null, 4));
    })

Ve výše uvedeném kanálu je prvním krokem $unwind operátor

{ "$unwind": "$products" }

což je docela užitečné, když jsou data uložena jako pole. Když je operátor odvíjení aplikován na datové pole seznamu, vygeneruje nový záznam pro každý prvek datového pole seznamu, na který je aplikováno odvíjení. V podstatě zplošťuje data.

Toto je nezbytná operace pro další fázi kanálu, $group krok, ve kterém seskupujete sloučené dokumenty podle _id pole, čímž efektivně přeskupíte denormalizované dokumenty zpět do jejich původního schématu.

$group Operátor potrubí je podobný SQL GROUP BY doložka. V SQL nemůžete použít GROUP BY pokud nepoužíváte některou z agregačních funkcí. Stejně tak musíte v MongoDB použít agregační funkci (nazývanou akumulátory). Další informace o akumulátorech si můžete přečíst zde .

V této $skupině operace, logika pro výpočet totalPrice a vrácení původních polí je prostřednictvím akumulátorů . Získáte totalPrice sečtením každého jednotlivého mezisoučtu hodnoty na skupinu s $sum jako:

"totalPrice": { "$sum": "$products.subTotal }

Druhý výraz

"userPurchased": { "$first": "$userPurchased" },

vrátí userPurchased hodnotu z prvního dokumentu pro každou skupinu pomocí $first . Efektivně tak znovu sestaví původní schéma dokumentu před $unwind

Jedna věc, kterou je třeba poznamenat, je, že při provádění kanálu MongoDB spojuje operátory navzájem. "Pipe" zde má význam Linux:výstup operátora se stává vstupem následujícího operátora. Výsledkem každého operátora je nová sbírka listin. Mongo tedy provede výše uvedený kanál následovně:

collection | $unwind | $group => result

Jako vedlejší poznámku, chcete-li pomoci s pochopením kanálu nebo jej ladit, pokud byste získali neočekávané výsledky, spusťte agregaci pouze s prvním operátorem kanálu. Například spusťte agregaci v mongo shell jako:

db.cart.aggregate([
    { "$unwind": "$products" }
])

Zkontrolujte výsledek, zda produkty pole je správně dekonstruováno. Pokud to dává očekávaný výsledek, přidejte následující:

db.cart.aggregate([
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$_id",
            "products": { "$push": "$products" },
            "userPurchased": { "$first": "$userPurchased" },
            "totalPrice": { "$sum": "$products.subTotal" }
        }
    }
])

Opakujte kroky, dokud se nedostanete ke konečnému kroku potrubí.

Pokud chcete pole aktualizovat, můžete přidat $out fáze potrubí jako poslední krok. Tím se výsledné dokumenty agregačního kanálu zapíší do stejné kolekce, čímž se kolekce technicky aktualizuje.

var pipeline = [
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$_id",
            "products": { "$push": "$products" },
            "userPurchased": { "$first": "$userPurchased" },
            "totalPrice": { "$sum": "$products.subTotal" }
        }
    },
    { "$out": "cart" } // write the results to the same underlying mongo collection
]

AKTUALIZACE

Chcete-li provést aktualizaci i dotaz, můžete zadat find() zavolejte agregované zpětné volání, abyste získali aktualizovaný soubor json, tj.

Cart.aggregate(pipeline)
    .exec(function(err, results){
        if (err) throw err;
        Cart.find().exec(function(err, docs){
            if (err) return handleError(err);
            console.log(JSON.stringify(docs, null, 4));
        })
    })
    

Pomocí Promises to můžete udělat alternativně jako

Cart.aggregate(pipeline).exec().then(function(res)
    return Cart.find().exec();
).then(function(docs){  
    console.log(JSON.stringify(docs, null, 4));
});


  1. Sestavit tento LEFT() SQL v dotazu MongoDB?

  2. Persist and flush - Doctrine a MongoDB

  3. Jak nainstalovat MongoDB

  4. Push prvek v poli, který je klíčem jiného objektu na určité pozici