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

Summen af ​​underdokumenter i Mongoose

Brug af aggregate() funktion, kan du køre følgende pipeline, som bruger $sum operatør for at få de ønskede resultater:

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

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

og den tilsvarende opdateringshandling følger:

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

Eller hvis du bruger MongoDB 3.2 og tidligere versioner, hvor $sum er kun tilgængelig i $group-stadiet, kan du gøre

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 ovenstående pipeline er det første trin $unwind operatør

{ "$unwind": "$products" }

hvilket er ret praktisk, når dataene er gemt som et array. Når afviklingsoperatoren anvendes på et listedatafelt, genererer den en ny post for hvert element i listedatafeltet, som afvikling er anvendt på. Det flader grundlæggende dataene ud.

Dette er en nødvendig operation for det næste pipelinetrin, $gruppe trin, hvor du grupperer de fladtrykte dokumenter efter _id felt og dermed effektivt omgruppere de denormaliserede dokumenter tilbage til deres oprindelige skema.

$group pipeline-operatoren ligner SQL's GROUP BY klausul. I SQL kan du ikke bruge GROUP BY medmindre du bruger nogen af ​​aggregeringsfunktionerne. På samme måde skal du bruge en aggregeringsfunktion i MongoDB (kaldet akkumulatorer). Du kan læse mere om akkumulatorerne her .

I denne $group operation, logikken til at beregne totalPrice og returnering af de originale felter sker gennem akkumulatorerne . Du får totalPrice ved at opsummere hver enkelt subTotal værdier pr. gruppe med $sum som:

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

Det andet udtryk

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

returnerer en userPurchased værdi fra det første dokument for hver gruppe ved hjælp af $first . Derved genopbygges det originale dokumentskema effektivt før $unwind

En ting at bemærke her er, når man udfører en pipeline, MongoDB piper operatører ind i hinanden. "Rør" har her Linux-betydningen:output fra en operatør bliver input fra følgende operatør. Resultatet af hver operatør er en ny samling af dokumenter. Så Mongo udfører ovenstående pipeline som følger:

collection | $unwind | $group => result

Som en sidebemærkning, for at hjælpe med at forstå pipelinen eller fejlfinde den, hvis du får uventede resultater, skal du køre aggregeringen med kun den første pipeline-operatør. Kør f.eks. aggregeringen i mongo-shell som:

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

Tjek resultatet for at se, om produkterne array er dekonstrueret korrekt. Hvis det giver det forventede resultat, skal du tilføje det næste:

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

Gentag trinene, indtil du kommer til det sidste pipeline-trin.

Hvis du vil opdatere feltet, kan du tilføje $out pipeline-fasen som det sidste trin. Dette vil skrive de resulterende dokumenter fra aggregeringspipelinen til den samme samling og dermed teknisk opdatere 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
]

OPDATERING

For at udføre både opdateringen og forespørgslen kan du derefter udstede en find() ring ind det samlede tilbagekald for at få den opdaterede 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));
        })
    })
    

Ved at bruge Promises kan du gøre dette alternativt som

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


  1. Maven kører ikke med Spigot

  2. SailsJS &MongoDB Aggregation-rammeproblemer med tilpassede forespørgsler

  3. Spring Data MongoRepository gemmer objekter med forskelligt antal felter

  4. Hvordan kontrollerer jeg, om et indeks bliver brugt