Изглежда, че сте започнали с това, но сте се изгубили в някои от другите концепции. Има някои основни истини при работа с масиви в документи, но нека започнем оттам, откъдето спряхте:
db.sample.aggregate([
{ "$group": {
"_id": "$status",
"count": { "$sum": 1 }
}}
])
Така че просто ще използвам $group
тръбопровод, за да събере вашите документи за различните стойности на полето "статус" и след това да създаде друго поле за "брой", което разбира се "брои" срещанията на ключа за групиране чрез предаване на стойност 1
към $sum
оператор за всеки намерен документ. Това ви поставя в точка, подобна на описаната от вас:
{ "_id" : "done", "count" : 2 }
{ "_id" : "canceled", "count" : 1 }
Това е първият етап от това и е достатъчно лесен за разбиране, но сега трябва да знаете как да извличате стойности от масив. Тогава може да се изкушите, след като разберете "нотация с точки" концепция, за да направите нещо подобно:
db.sample.aggregate([
{ "$group": {
"_id": "$status",
"count": { "$sum": 1 },
"total": { "$sum": "$devices.cost" }
}}
])
Но това, което ще откриете е, че „общата сума“ всъщност ще бъде 0
за всеки от тези резултати:
{ "_id" : "done", "count" : 2, "total" : 0 }
{ "_id" : "canceled", "count" : 1, "total" : 0 }
Защо? Ами операциите за агрегиране на MongoDB като тази всъщност не преминават през елементите на масива при групиране. За да направи това, рамката за агрегиране има концепция, наречена $unwind
. Името е относително самообясняващо се. Вграденият масив в MongoDB прилича много на асоцииране „един към много“ между свързани източници на данни. И така, какво $unwind
прави е точно този вид резултат от „съединяване“, където получените „документи“ са базирани на съдържанието на масива и дублираната информация за всеки родител.
Така че, за да действате върху елементите на масива, трябва да използвате $unwind
първи. Това логично трябва да ви доведе до код като този:
db.sample.aggregate([
{ "$unwind": "$devices" },
{ "$group": {
"_id": "$status",
"count": { "$sum": 1 },
"total": { "$sum": "$devices.cost" }
}}
])
И след това резултатът:
{ "_id" : "done", "count" : 4, "total" : 700 }
{ "_id" : "canceled", "count" : 2, "total" : 350 }
Но това не е съвсем правилно, нали? Спомнете си какво току-що научихте от $unwind
и как прави денормализирано съединяване с родителската информация? Така че сега това се дублира за всеки документ, тъй като и двата имаха два члена на масива. Така че докато полето „общо“ е правилно, „броят“ е два пъти повече, отколкото трябва да бъде във всеки случай.
Трябва да се обърне малко повече внимание, така че вместо да правите това в един $group
етап, той се извършва на два:
db.sample.aggregate([
{ "$unwind": "$devices" },
{ "$group": {
"_id": "$_id",
"status": { "$first": "$status" },
"total": { "$sum": "$devices.cost" }
}},
{ "$group": {
"_id": "$status",
"count": { "$sum": 1 },
"total": { "$sum": "$total" }
}}
])
Което сега получава резултата с правилни суми в него:
{ "_id" : "canceled", "count" : 1, "total" : 350 }
{ "_id" : "done", "count" : 2, "total" : 700 }
Сега цифрите са правилни, но все още не е точно това, което питате. Мисля, че трябва да спрете дотук, тъй като видът резултат, който очаквате, наистина не е подходящ само за един резултат само от агрегиране. Търсите общата сума да е "вътре" в резултата. Наистина не му е мястото там, но при малки данни е добре:
db.sample.aggregate([
{ "$unwind": "$devices" },
{ "$group": {
"_id": "$_id",
"status": { "$first": "$status" },
"total": { "$sum": "$devices.cost" }
}},
{ "$group": {
"_id": "$status",
"count": { "$sum": 1 },
"total": { "$sum": "$total" }
}},
{ "$group": {
"_id": null,
"data": { "$push": { "count": "$count", "total": "$total" } },
"totalCost": { "$sum": "$total" }
}}
])
И форма за краен резултат:
{
"_id" : null,
"data" : [
{
"count" : 1,
"total" : 350
},
{
"count" : 2,
"total" : 700
}
],
"totalCost" : 1050
}
Но „Не правете това“ . MongoDB има ограничение на документа за отговор от 16MB, което е ограничение на BSON спецификацията. При малки резултати можете да направите този вид удобно обвиване, но в по-голямата схема от неща искате резултатите в по-ранна форма и или отделна заявка, или живо с повторение на целите резултати, за да получите общата сума от всички документи.
Изглежда, че използвате версия на MongoDB, по-малка от 2.6, или копирате изход от обвивка на RoboMongo, която не поддържа функциите на най-новата версия. От MongoDB 2.6 обаче резултатите от агрегацията могат да бъдат „курсор“, а не единичен BSON масив. Така че общият отговор може да бъде много по-голям от 16 MB, но само когато не компресирате до един документ като резултати, показани за последния пример.
Това би било особено вярно в случаите, когато сте „страницирали“ резултатите със 100 до 1000 реда с резултати, но просто сте искали „общо“ да се върне в отговор на API, когато връщате само „страница“ от 25 резултата на време.
Както и да е, това трябва да ви даде разумно ръководство за това как да получите типа резултати, които очаквате от вашия общ формуляр на документ. Запомнете $unwind
за да обработваме масиви и като цяло $group
няколко пъти, за да получите общи суми на различни нива на групиране от вашите групирания на документи и колекции.