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

Somma dei documenti secondari in Mongoose

Utilizzo di aggregate() funzione, è possibile eseguire la pipeline seguente che utilizza $sum operatore per ottenere i risultati desiderati:

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

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

e la corrispondente operazione di aggiornamento segue:

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

O se si utilizza MongoDB 3.2 e versioni precedenti, dove $sum è disponibile solo nella fase a gironi $, puoi farlo

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

Nella pipeline di cui sopra, il primo passaggio è il $unwind operatore

{ "$unwind": "$products" }

che è abbastanza utile quando i dati vengono archiviati come un array. Quando l'operatore di rimozione viene applicato a un campo dati elenco, genererà un nuovo record per ogni elemento del campo dati elenco su cui è applicato lo svolgimento. Fondamentalmente appiattisce i dati.

Questa è un'operazione necessaria per la fase successiva della pipeline, il $group passaggio in cui raggruppi i documenti appiattiti per _id campo, raggruppando così efficacemente i documenti denormalizzati nel loro schema originale.

Il $group l'operatore della pipeline è simile a GROUP BY di SQL clausola. In SQL, non puoi usare GROUP BY a meno che non si utilizzi una delle funzioni di aggregazione. Allo stesso modo, devi usare anche una funzione di aggregazione in MongoDB (chiamata accumulatori). Puoi leggere ulteriori informazioni sugli accumulatori qui .

In questo $group operazione, la logica per calcolare il totalPrice e la restituzione dei campi originali avviene tramite gli accumulatori . Ottieni il totalPrice sommando ogni singolo subTotal valori per gruppo con $sum come:

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

L'altra espressione

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

restituirà un userPurchased valore dal primo documento per ogni gruppo che utilizza $first . Ricostruendo così efficacemente lo schema del documento originale prima del $unwind

Una cosa da notare qui è che quando si esegue una pipeline, MongoDB convoglia gli operatori l'uno nell'altro. "Pipe" qui assume il significato di Linux:l'output di un operatore diventa l'input dell'operatore successivo. Il risultato di ogni operatore è una nuova raccolta di documenti. Quindi Mongo esegue la pipeline di cui sopra come segue:

collection | $unwind | $group => result

Come nota a margine, per aiutare a comprendere la pipeline o per eseguirne il debug in caso di risultati imprevisti, eseguire l'aggregazione solo con il primo operatore della pipeline. Ad esempio, esegui l'aggregazione in mongo shell come:

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

Controlla il risultato per vedere se i products l'array è decostruito correttamente. Se questo dà il risultato atteso, aggiungi il prossimo:

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

Ripeti i passaggi fino ad arrivare al passaggio finale della pipeline.

Se desideri aggiornare il campo, puoi aggiungere il $out fase della pipeline come ultimo passaggio. Questo scriverà i documenti risultanti della pipeline di aggregazione nella stessa raccolta, aggiornando così tecnicamente la raccolta.

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
]

AGGIORNAMENTO

Per eseguire sia l'aggiornamento che la query, puoi quindi emettere un find() chiama nella richiamata aggregata per ottenere il json aggiornato, ad esempio

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

Usando Promises, puoi farlo in alternativa come

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