sql >> Database >  >> NoSQL >> MongoDB

Som van subdocumenten in Mongoose

De aggregate() gebruiken functie, kunt u de volgende pijplijn uitvoeren die de $sum operator om de gewenste resultaten te krijgen:

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

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

en de bijbehorende update-bewerking volgt:

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

Of als u MongoDB 3.2 en eerdere versies gebruikt, waarbij $sum is alleen beschikbaar in de $group-fase, dat kan je doen

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

In de bovenstaande pijplijn is de eerste stap de $unwind telefoniste

{ "$unwind": "$products" }

wat erg handig is als de gegevens als een array worden opgeslagen. Wanneer de afwikkeloperator wordt toegepast op een lijstgegevensveld, genereert deze een nieuw record voor elk element van het lijstgegevensveld waarop afwikkelen wordt toegepast. Het maakt de gegevens in feite plat.

Dit is een noodzakelijke bewerking voor de volgende pijplijnfase, de $group stap waar u de afgeplatte documenten groepeert op de _id veld, waardoor de gedenormaliseerde documenten effectief worden hergroepeerd naar hun oorspronkelijke schema.

De $group pijplijnoperator is vergelijkbaar met GROUP BY . van de SQL clausule. In SQL kunt u GROUP BY . niet gebruiken tenzij u een van de aggregatiefuncties gebruikt. Op dezelfde manier moet u ook een aggregatiefunctie in MongoDB gebruiken (genaamd accumulatoren). U kunt hier meer lezen over de accumulatoren .

In deze $group operatie, de logica om de totalPrice . te berekenen en het retourneren van de originele velden is via de accumulatoren . U krijgt detotalPrice door elk afzonderlijk subTotal bij elkaar op te tellen waarden per groep met $sum als:

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

De andere uitdrukking

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

retourneert een userPurchased waarde uit het eerste document voor elke groep met $first . Zo wordt het originele documentschema effectief opnieuw opgebouwd vóór de $unwind

Een ding om op te merken is dat bij het uitvoeren van een pijplijn, MongoDB operators in elkaar laat lopen. "Pipe" heeft hier de Linux-betekenis:de uitvoer van een operator wordt de invoer van de volgende operator. Het resultaat van elke operator is een nieuwe verzameling documenten. Dus Mongo voert de bovenstaande pijplijn als volgt uit:

collection | $unwind | $group => result

Als een kanttekening, om te helpen bij het begrijpen van de pijplijn of om deze te debuggen als u onverwachte resultaten krijgt, voert u de aggregatie uit met alleen de eerste pijplijnoperator. Voer de aggregatie bijvoorbeeld uit in mongo-shell als:

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

Controleer het resultaat om te zien of de products array correct is gedeconstrueerd. Als dat het verwachte resultaat geeft, voeg dan het volgende toe:

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

Herhaal de stappen totdat u bij de laatste pijplijnstap komt.

Als u het veld wilt bijwerken, kunt u de $out pijplijnfase als laatste stap. Hierdoor worden de resulterende documenten van de aggregatiepijplijn naar dezelfde verzameling geschreven, waardoor de verzameling technisch wordt bijgewerkt.

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
]

UPDATE

Om zowel de update als de query uit te voeren, kunt u een find() . geven bel de totale callback om de bijgewerkte json te krijgen, d.w.z.

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

Als u Promises gebruikt, kunt u dit ook doen als

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


  1. MongoDB Schrijfzorg:3 Must-Know Caveats

  2. Mongoose-verbinding met replicaset

  3. JavaScript-ontwerppatronen -- Omgaan met ongewenste asynchronie

  4. hoe een automatisch ophogingsnummer in te voegen met mijn mangoestverzameling