Как я могу получить $ сумму за массив в документе?

Например, у меня есть следующий документ:

{
    "_id" : ObjectId("52ffddd3c69e40174c046d67"),
    "works" : [ 
        {
            "service_id" : ObjectId("52ffd576c69e40174c046d64"),
            "price" : 150,
            "count" : 3,
            "items" : [ 
                {
                    "price" : 5,
                    "amount" : 10
                }, 
                {
                    "price" : 7,
                    "amount" : 3
                }
            ]
        }, 
        {
            "service_id" : ObjectId("52ffd56fc69e40174c046d63"),
            "price" : 100,
            "count" : 2
        }
    ]
}

Где service_id относится к какому-то внешнему документу (я знаю, что это не рекомендуется), цена - это цена за работу, count - это количество выполненных работ, а items - массив для материалов, используемых во всех работах. Мне нужно получить общую стоимость всех работ. Я попробовал следующий незаконченный код:

db.service_cards.aggregate(
    { $match: {_id: ObjectId("52ffddd3c69e40174c046d67") } },
    { $unwind: "$works" },
    { $project: {
        "works.service_id": 1,
        "works.price": 1,
        "works.count": 1,
        "works.items": { 
            "$cond": [ 
                { $eq: [ "$works.items", []] },
                [{
                    "price": 0,
                    "amount": 0
                }],
                "$works.items"
            ],
        }
    } },
    { $project: {
        "works.service_id": 1,
        "works.price": 1,
        "works.count": 1,
        "works.items": { 
            "$ifNull": [ 
                "$works.items",
                [{
                    "price": 0,
                    "amount": 0
                }]
            ],
        }
    } },
    { $unwind: "$works.items" },
    { $project: { 
        "service_id": "$works.service_id",
        "price": "$works.price",
        "count": "$works.count",
        "items_cost": { $multiply: ["$works.items.price", "$works.items.amount"] },
    } },
    { $group: { 
        _id: "$service_id", 
        "sum_items": { $sum: "$items_cost" },
        "price": { $avg: "$price" },
        "count": { $avg: "$count" }
    } }
)

На самом деле он не суммирует массивы - он раскручивает работы и предметы, а затем использует $ group для суммирования неиспользованных предметов. Но потом я вспомнил - может быть две работы с одинаковыми service_id, count и price, и после $ unwind и $ group я потеряю одну из них, соединив предметы из обеих.

Есть ли способ получить массив $ sum внутри документа без $ unwind?

Изменить: Я ожидаю получить список работ без их предметов, но с общей стоимостью предметов:

{
    "_id" : ObjectId("52ffddd3c69e40174c046d67"),
    "works" : [ 
        {
            "service_id" : ObjectId("52ffd576c69e40174c046d64"),
            "price" : 150,
            "count" : 3,
            "items_cost" : 71
        }, 
        {
            "service_id" : ObjectId("52ffd56fc69e40174c046d63"),
            "price" : 100,
            "count" : 2,
            "items_cost": 0
        }
    ]
}

1 ответ

Решение

Я думаю, это не лучшее решение, но оно короткое и делает то, что вы хотите:

db.service_cards.find().forEach(function (document) {
    var works = document.works || [];
    var result = [];
    works.forEach(function (work) {
        var items = work.items || [];
        var itemsCost = 0;
        items.forEach(function (item) {
            itemsCost += item.price * item.amount;
        });
        var resultObj = {
            service_id: work.service_id,
            price: work.price,
            count: work.count,
            items_cost: itemsCost
        };
        result.push(resultObj);
    });
    print({
        _id: document._id,
        works: result
    });
})
Другие вопросы по тегам