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

Групирайте и пребройте, като използвате рамка за агрегиране

Изглежда, че сте започнали с това, но сте се изгубили в някои от другите концепции. Има някои основни истини при работа с масиви в документи, но нека започнем оттам, откъдето спряхте:

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 няколко пъти, за да получите общи суми на различни нива на групиране от вашите групирания на документи и колекции.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Външното поле на $lookup може да бъде полето на вложен документ?

  2. Функцията за изтриване не работи с angularJS

  3. Най-лесният начин за инсталиране на Mongodb PHP разширение в Ubuntu 13.10 (дръзко)?

  4. Инсталиране на Mongodb с Lamp на Ubuntu (Linux)

  5. Как да проверите дали даден индекс е скрит в MongoDB