MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Сума от поддокументи в Mongoose

Използване на aggregate() функция, можете да стартирате следния конвейер, който използва $sum оператор, за да получите желаните резултати:

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

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

и съответната операция за актуализиране следва:

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

Или ако използвате MongoDB 3.2 и по-стари версии, където $sum е наличен само в етап $group, можете да направите

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

В горния конвейер първата стъпка е $unwind оператор

{ "$unwind": "$products" }

което е доста удобно, когато данните се съхраняват като масив. Когато операторът unwind се приложи към поле с данни на списък, той ще генерира нов запис за всеки елемент от полето с данни на списъка, към който се прилага unwind. По същество изравнява данните.

Това е необходима операция за следващия етап на конвейера, $group стъпка, в която групирате сплесканите документи по _id поле, като по този начин ефективно прегрупира денормализираните документи обратно към оригиналната им схема.

$group конвейерният оператор е подобен на SQL GROUP BY клауза. В SQL не можете да използвате GROUP BY освен ако не използвате някоя от функциите за агрегиране. По същия начин трябва да използвате и функция за агрегиране в MongoDB (наречена акумулатори). Можете да прочетете повече за акумулаторите тук .

В тази $group операция, логиката за изчисляване на totalPrice и връщането на оригиналните полета е чрез акумулаторите . Получавате totalPrice чрез сумиране на всеки отделен subTotal стойности на група с $sum като:

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

Другият израз

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

ще върне userPurchased стойност от първия документ за всяка група с помощта на $first . По този начин се възстановява ефективно оригиналната схема на документа преди $unwind

Едно нещо, което трябва да се отбележи тук е, че когато се изпълнява конвейер, MongoDB прехвърля операторите един в друг. „Тръба“ тук приема значението на Linux:изходът на оператор става вход на следващия оператор. Резултатът от всеки оператор е нова колекция от документи. Така Mongo изпълнява горния тръбопровод по следния начин:

collection | $unwind | $group => result

Като странична бележка, за да помогнете с разбирането на тръбопровода или да го отстраните, ако получите неочаквани резултати, стартирайте агрегацията само с първия оператор на тръбопровода. Например, стартирайте агрегацията в mongo shell като:

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

Проверете резултата, за да видите дали продуктите масивът се деконструира правилно. Ако това даде очаквания резултат, добавете следното:

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

Повторете стъпките, докато стигнете до последната стъпка на тръбопровода.

Ако искате да актуализирате полето, можете да добавите $out тръбопроводен етап като последна стъпка. Това ще запише получените документи от тръбопровода за агрегиране в същата колекция, като по този начин технически актуализира колекцията.

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
]

АКТУАЛИЗАЦИЯ

За да извършите както актуализацията, така и заявката, можете след това да подадете find() извикайте обобщеното обратно извикване, за да получите актуализирания json, т.е.

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

Използвайки Promises, можете да направите това алтернативно като

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


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Как да търсите запис и след това да го изтриете

  2. Бази данни, използващи JSON като формат за съхранение/транспорт

  3. Вмъкване и извличане на дати и времеви марки в mongodb с помощта на PHP

  4. Mongodb обобщава (брои) в множество полета едновременно

  5. Mongoose findById() в обект от вложени схеми/поддокументи - агрегиране