sql >> Databasteknik >  >> NoSQL >> MongoDB

Summan av underdokument i Mongoose

Använda aggregate() kan du köra följande pipeline som använder $summa för att få önskat resultat:

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

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

och motsvarande uppdateringsåtgärd följer:

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

Eller om du använder MongoDB 3.2 och tidigare versioner, där $sum är endast tillgänglig i $group-stadiet, du kan göra

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

I ovanstående pipeline är det första steget $unwind operatör

{ "$unwind": "$products" }

vilket är ganska praktiskt när data lagras som en array. När avvecklingsoperatorn appliceras på ett listdatafält, kommer den att generera en ny post för varje element i listdatafältet som avveckling tillämpas på. Det plattar i princip ut data.

Detta är en nödvändig operation för nästa steg i pipeline, $grupp steg där du grupperar de tillplattade dokumenten efter _id fältet, vilket effektivt omgrupperar de denormaliserade dokumenten tillbaka till deras ursprungliga schema.

$group pipeline-operatorn liknar SQL:s GROUP BY klausul. I SQL kan du inte använda GROUP BY såvida du inte använder någon av aggregeringsfunktionerna. På samma sätt måste du också använda en aggregeringsfunktion i MongoDB (kallas ackumulatorer). Du kan läsa mer om ackumulatorerna här .

I denna $group operation, logiken för att beräkna totalPrice och de ursprungliga fälten returneras genom ackumulatorerna . Du får totalPrice genom att summera varje enskild subTotal värden per grupp med $sum som:

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

Det andra uttrycket

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

returnerar en userPurchased värde från det första dokumentet för varje grupp med $first . På så sätt återuppbyggs det ursprungliga dokumentschemat före $unwind

En sak att notera här är när man kör en pipeline, MongoDB kopplar operatörer till varandra. "Pipe" har här Linux-betydelsen:utdata från en operatör blir indata för följande operatör. Resultatet av varje operatör är en ny samling dokument. Så Mongo kör ovanstående pipeline enligt följande:

collection | $unwind | $group => result

Som en sidoanteckning, för att hjälpa dig att förstå pipelinen eller för att felsöka den om du får oväntade resultat, kör aggregeringen med bara den första pipeline-operatören. Kör till exempel aggregeringen i mongo-skalet som:

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

Kontrollera resultatet för att se om produkterna arrayen är korrekt dekonstruerad. Om det ger det förväntade resultatet, lägg till nästa:

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

Upprepa stegen tills du kommer till det sista pipelinesteget.

Om du vill uppdatera fältet kan du lägga till $out pipeline skede som det sista steget. Detta kommer att skriva de resulterande dokumenten för aggregeringspipelinen till samma samling, vilket tekniskt uppdaterar samlingen.

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
]

UPPDATERA

För att göra både uppdateringen och frågan kan du sedan utfärda en find() ring in den samlade återuppringningen för att få den uppdaterade json, dvs.

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

Med Promises kan du göra detta alternativt som

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


  1. Ta bort dubbletter från MongoDB 4.2-databasen

  2. Mongo URI med självsignerat certifikat

  3. MongoDB PHP-drivrutin och MongoDB-kompatibilitetskontroll

  4. mongodb dra objekt från en array som inte fungerar med mongoose