MongoDB - Aggregation (Agregacija)

Agregacija predstavlja proces u sklopu koga se vrše određene operacije nad dokumentima i vraća obrađen razultat.
Za vršenje agregacije unutar MongoDB koristi se MongoDB metoda aggregate().
Sintaksa je db.COLLECTION-NAME.aggregate({PIPELINE-OPTIONS}, {PIPELINE-OPTIONS}, ....).

Pre svega treba biti upoznat sa Pipeline (cevovod) konceptom.To predstavlja mogućnost da se izvrše određene operaije nad input-om a zatim da se taj output iskoristi kao input za sledeću komandu i tako dalje. Jednostavno rečeno - nadovezivanje.

"Aggregation Pipeline" je "framework" koji izvršava agregaciju za nas. Kada se koristi Aggregation framework, MongoDB prosledi dokument kroz "pipeline", unutar njega dokument prolazi kroz određene "faze", u svakoj fazi se dokument transformiše na odgovarajući način da bi se na kraju dobio željeno obrađeni rezultat.

Aggregation Pipeline Stages

U sledećoj tabeli prikazane su faze agregacionog "pipeline-a". (Za celokupnu listu posetite MongoDB sajt).

Stage Opis
$project Prosleđuje polje sledećoj fazi sa postojećim ili novim poljima. Polja se mogu dodavati dinamičkim.
$match Filtrira dokumenta i proseđuje samo ona odgovarajuća sledećoj fazi.
$limit Prosleđuje X nemodifikovanih dokumenata i prosleđuje ih sledećoj fazi. X predstavlja broj (limit) dokumenata koji će biti prosleđeni.
$group Grupiše određene dokumente i prosleđuje ih sledećoj fazi agregacionog pipeline-a.
$sort Menja redosled dokumenata. (Rastući ili opadajući)
$sum Sabiranje numeričkih vrednosti.
$lookup Izvršava "left out join" sa drugom kolekcijom unutar iste baze podataka.

Zadatak 32 - Prikazati sve studente kolekcije student koji studiraju na smeru "ISiT" (koristeći aggregation)

U ovakvim slučajevima kada je potrebno filtrirati datu i proslediti isfiltrirani deo u sledeću fazu agregacionog "pipeline-a" koristimo $match. $match filtrira datu i prosleđuje odgovarajuću datu dalje.

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$match":{"smer":"ISiT"}}]).pretty()
{
    "_id" : ObjectId("5902012ebb26ec62d0b6bd2e"),
    "student_id" : "2012/0161",
    "ime" : "Daniel Kostic",
    "smer" : "ISiT",
    "polozeni_ispiti" : [
        {
            "predmet" : "AROS", 
            "ocena" : "6",
            "datumPolaganja" : "4.3.2013",
            "semestar" : "III"
        },
        {
            "predmet" : "Strukture podataka i algoritmi",
            "ocena" : "9",
            "datumPolaganja" : "13.4.2014",
            "semestar" : "IV"
        },
        {
            "predmet" : "PIS",
            "ocena" : "10",
            "datumPolaganja" : "10.1.2016",
            "semestar" : "VII"
        },
        {
            "predmet" : "Upravljanje kvalitetom",
            "ocena" : "8",
            "datumPolaganja" : "16.9.2016",
            "semestar" : "VIII"
        }
    ],
    "grad" : "Brzece",
    "Postanski_broj" : "54000",
    "srednja_skola" : {
        "grad" : "Brus",
        "tip" : "Gimnazija",
        "naziv" : "Gimnazija u Brusu",
        "poeni_iz_skole" : "37.5"
    }
}
...

Zadatak 33 - Vratiti sve studente iz kolekcije _student _koji studiraju smer "ISiT" i koji su imali više od 38 poena u srednjoj školi (koristeći aggregation)

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$match":{"$and":[{"smer":"ISiT"}, {"srednja_skola.poeni_iz_skole":{"$gt": "38"}}]}}]).pretty()
{
    "_id" : ObjectId("5902012ebb26ec62d0b6bd2f"),
    "student_id" : "2012/0112",
    "ime" : "Emilie Eric",
    "smer" : "ISiT",
    "polozeni_ispiti" : [
        {
            "predmet" : "EPOS",
            "ocena" : "10",
            "datumPolaganja" : "20.3.2015",
            "semestar" : "V"
        },
        {
            "predmet" : "ITEH",
            "ocena" : "9",
            "datumPolaganja" : "20.4.2016",
            "semestar" : "VII"
        },
        {
            "predmet" : "IIU",
            "ocena" : "7",
            "datumPolaganja" : "21.5.2016",
            "semestar" : "VIII"
        },
        {
            "predmet" : "Internet Marketing",
            "ocena" : "8",
            "datumPolaganja" : "15.7.2016",
            "semestar" : "VIII"
        }
    ],
    "grad" : "Cacak",
    "Postanski_broj" : "32000",
    "srednja_skola" : {
        "grad" : "Cacak",
        "tip" : "Gimnazija",
        "naziv" : "Gimnazija u Cacku",
        "poeni_iz_skole" : "40"
    }
}
....

Zadatak 34 - Vratiti imena i grad iz kojeg dolaze svih studenata iz kolekcije student (koristeći aggregation)

Za ovakve potrebe koristi se $project . On omogućava oblikovanje dokumenata dodavanjem, brisanjem ili menjanjem imena polja i takve prosleđuje u sledeće faze "pipeline-a". Koristi se 1 ili true da se uračuna polje, odnosno 0 ili false da se obriše.

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$project":{"ime":1, "grad":1}}])
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd2e"), "ime" : "Daniel Kostic", "grad" : "Brzece" }
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd2f"), "ime" : "Emilie Eric", "grad" : "Cacak" }
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd30"), "ime" : "Milos Nikolic", "grad" : "Beograd" }
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd31"), "ime" : "Jovana Pavlovic", "grad" : "Kraljevo" }
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd32"), "ime" : "Petar Jelenkovic", "grad" : "Beograd" }
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd33"), "ime" : "Nadica Dimitrijevic", "grad" : "Petrovac na Mlavi" }
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd34"), "ime" : "Nikola Music", "grad" : "Sarajevo" }
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd35"), "ime" : "Nadja Jelicic", "grad" : "Brus" }
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd36"), "ime" : "Marija Jankovic", "grad" : "Nis" }
{ "_id" : ObjectId("5902012ebb26ec62d0b6bd37"), "ime" : "Rastko Cerge", "grad" : "Beograd" }

Zadatak 35 - Vratiti imena i grad iz kojeg dolaze svih studenata koji studiraju na smeru "Menadzemnt" iz kolekcije student (koristeći aggregation)

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$match":{"smer":"Menadzment"}},{"$project":{"ime":1, "grad":1,"_id":0}}])
{ "ime" : "Petar Jelenkovic", "grad" : "Beograd" }
{ "ime" : "Rastko Cerge", "grad" : "Beograd" }

Zadatak 36 - Prikazati svaki položeni predmet studenta sa imenom "Daniel Kostic" unutar kolekcije student kao poseban dokument

Za ovakve potrebe koristi se faza $unwind. $unwind kreira novi dokument sa svaki element u nizu. On vraća novi dokument koji predstavlja listu novih dokumenata, a svaki novi dokument je u stvari element niza nad kojim se $unwind izvršio.

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$match": {"ime": "Daniel Kostic"}},{"$unwind": "$polozeni_ispiti"}]).pretty()
{
    "_id" : ObjectId("5902012ebb26ec62d0b6bd2e"),
    "student_id" : "2012/0161",
    "ime" : "Daniel Kostic",
    "smer" : "ISiT",
    "polozeni_ispiti" : {
        "predmet" : "AROS",
        "ocena" : "6",
        "datumPolaganja" : "4.3.2013",
        "semestar" : "III"
    },
    "grad" : "Brzece",
    "Postanski_broj" : "54000",
    "srednja_skola" : {
        "grad" : "Brus",
        "tip" : "Gimnazija",
        "naziv" : "Gimnazija u Brusu",
        "poeni_iz_skole" : "37.5"
    }
}
{
    "_id" : ObjectId("5902012ebb26ec62d0b6bd2e"),
    "student_id" : "2012/0161",
    "ime" : "Daniel Kostic",
    "smer" : "ISiT",
    "polozeni_ispiti" : {
        "predmet" : "Strukture podataka i algoritmi",
        "ocena" : "9",
        "datumPolaganja" : "13.4.2014",
        "semestar" : "IV"
    },
    "grad" : "Brzece",
    "Postanski_broj" : "54000",
    "srednja_skola" : {
        "grad" : "Brus",
        "tip" : "Gimnazija",
        "naziv" : "Gimnazija u Brusu",
        "poeni_iz_skole" : "37.5"
    }
}
...

Dodatak

Kako je MongoDB "Schema less" moguće je da dokument ne sadrži određeni niz, ili je taj niz prazan, u tom slučaju svakako ne morate da brinete o tome šta $unwind radi u tim slučajevima, pre je MongoDB prikazivao grešku ali od MongoDB 3.2. ti dokumenti se jednostavno samo preskoče.


Zadatak 37 - Prikazati svaki položeni predmet studenta sa imenom "Daniel Kostic" unutar kolekcije student kao poseban dokument pri čemu će uz predmet prikazati samo smer na kojem studira i grad iz kojeg dolazi

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$match":{"ime": "Daniel Kostic"}},{"$unwind":"$polozeni_ispiti"},{"$project":{"ime":1, "smer":1, "grad":1, "polozeni_ispiti":1,  "_id": 0}}]).pretty()
{
    "ime" : "Daniel Kostic",
    "smer" : "ISiT",
    "polozeni_ispiti" : {
        "predmet" : "AROS",
        "ocena" : "6",
        "datumPolaganja" : "4.3.2013",
        "semestar" : "III"
    },
    "grad" : "Brzece"
}
{
    "ime" : "Daniel Kostic",
    "smer" : "ISiT",
    "polozeni_ispiti" : {
        "predmet" : "Strukture podataka i algoritmi",
        "ocena" : "9",
        "datumPolaganja" : "13.4.2014",
        "semestar" : "IV"
    },
    "grad" : "Brzece"
}
...

Zadatak 38 - Prikazati ukupan broj položenih ispita svih studenata smera "ISiT" i smera "Menadzment" kolekcije student

Za ovakve slučajeve možemo koristi $group. MongoDB koristi $group za grupisanje dokumenata po nekim specifičnim zahtevima. Grupisanje nije moguće obaviti u MongoDB bez agregacione funkcije.

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$group":{"_id":{"smer": "$smer"}, "ukupno_polozenih_ispita":{"$sum": {"$size": "$polozeni_ispiti"}}}}])
{ "_id" : { "smer" : "Menadzment" }, "ukupno_polozenih_ispita" : 8 }
{ "_id" : { "smer" : "ISiT" }, "ukupno_polozenih_ispita" : 32 }

Dodatak

Ovde je _id obavezno polje. Unutar njega prosleđujemo polje na osnovu koga želimo da grupišemo dokument.


Zadatak 39 - Prikazati ukupan broj položenih ispita svih studenata smera "ISiT" kolekcije student

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$match":{"smer": "ISiT"}},{"$group":{"_id":{"smer": "$smer"}, "ukupno_polozenih_ispita":{"$sum": {"$size": "$polozeni_ispiti"}}}}])
{ "_id" : { "smer" : "ISiT" }, "ukupno_polozenih_ispita" : 32 }

Zadatak 40 - Iskoristi prethodni upit ali ime "smer" promeniti u "ime_kursa" i "ukupno_polozenih_ispita" u "broj_ispita"

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$match":{"smer": "ISiT"}},{"$group":{"_id":{"smer": "$smer"}, "ukupno_polozenih_ispita":{"$sum": {"$size": "$polozeni_ispiti"}}}},{"$project": {"ime_kursa": "$_id.smer", "broj_ispita": "$ukupno_polozenih_ispita"}}])
{ "_id" : { "smer" : "ISiT" }, "ime_kursa" : "ISiT", "broj_ispita" : 32 }

Zadatak 41 - Prikazati imena studenata i ukupan broj njihovih položenih ispita u opadajućem redosledu (redosled na osnovu imena) iz kolekcije student

Za određivanje redosleda koristi se $sort koji sadrži ime polja na osnovu koga se vrši sortiranje i vrednost koja može biti 1 za rastući odnosno -1 za opadajući redosled.

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$group":{"_id":{"ime_studenta": "$ime"}, "ukupno_polozenih_ispita":{"$sum":{"$size": "$polozeni_ispiti"}}}},{"$sort": {"_id.ime_studenta": -1}}])
{ "_id" : { "ime_studenta" : "Rastko Cerge" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Petar Jelenkovic" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Nikola Music" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Nadja Jelicic" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Nadica Dimitrijevic" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Milos Nikolic" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Marija Jankovic" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Jovana Pavlovic" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Emilie Eric" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Daniel Kostic" }, "ukupno_polozenih_ispita" : 4 }

Zadatak 42 - Prikazati samo prva dva studenta iz prethodnog upita

Za limitiranje broja dokumenata koje aggregate vraća koristi se $limit koji prima samo vrednost, tj broj dokumenata koji treba da se vrati.

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$group":{"_id":{"ime_studenta": "$ime"}, "ukupno_polozenih_ispita":{"$sum":{"$size": "$polozeni_ispiti"}}}},{"$sort": {"_id.ime_studenta": -1}},{$limit: 2}])
{ "_id" : { "ime_studenta" : "Rastko Cerge" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Petar Jelenkovic" }, "ukupno_polozenih_ispita" : 4 }

Zadatak 43 - Prikazati četvrtog i petog studenta iz rezultata upita zadataka 41

Ovo ćemo ostvariti tako što ćemo preskočiti prva tri studenta iz rezultata koristeći $skip

Rešenje

use studenti
switched to db studenti
> db.student.aggregate([{"$group":{"_id":{"ime_studenta": "$ime"}, "ukupno_polozenih_ispita":{"$sum":{"$size": "$polozeni_ispiti"}}}},{"$sort": {"_id.ime_studenta": -1}},{"$skip":3},{"$limit": 2}])
{ "_id" : { "ime_studenta" : "Nadja Jelicic" }, "ukupno_polozenih_ispita" : 4 }
{ "_id" : { "ime_studenta" : "Nadica Dimitrijevic" }, "ukupno_polozenih_ispita" : 4 }

$lookup

$lookup dolazi sa verzijom MongoDB 3.2. i on je jedan od dugo očekivanih "features"-a, jer predstavlja isto što i joins u SQL-u. Pomoću $lookup moguće je spojiti dokumenta iz dve različite kolekcije.
Da bi odradili sledeće zadatake napravićemo novu bazu studenti_2 koja će imate dve kolekcije grad i student .

Fajlove grad.json i student_2.json skinite sa moodle naloga. https://github.com/elab-office/mongoDB/tree/master/json

User:~/ $ mongoimport --db studenti_2 --drop --collection student --file ~/desktop/student_2.json --jsonArray
2017-05-05T15:06:41.634+0200    connected to: localhost
2017-05-05T15:06:41.635+0200    dropping: studenti_2.student
2017-05-05T15:06:41.689+0200    imported 10 documents
User:~/ $ mongoimport --db studenti_2 --drop --collection grad --file ~/desktop/grad.json --jsonArray
2017-05-05T15:07:05.209+0200    connected to: localhost
2017-05-05T15:07:05.209+0200    dropping: studenti_2.grad
2017-05-05T15:07:05.266+0200    imported 8 documents

Zadatak 44 - Vratiti imena studenata i imena gradova odakle dolaze kombinujući kolekcije student i grad iz baze studenti_2

> use studenti_2
switched to db studenti_2
> db.student.aggregate([{$lookup: {from:"grad", localField:"grad_id", foreignField:"_id", as: "grad"}},{$project: {"ime":1, "grad.ime":1, "_id":0}}]).pretty()
{ "ime" : "Milos Nikolic", "grad" : [ { "ime" : "Beograd" } ] }
{ "ime" : "Rastko Cerge", "grad" : [ { "ime" : "Beograd" } ] }
{ "ime" : "Jovana Pavlovic", "grad" : [ { "ime" : "Kraljevo" } ] }
{ "ime" : "Daniel Kostic", "grad" : [ ] }
{ "ime" : "Petar Jelenkovic", "grad" : [ { "ime" : "Beograd" } ] }
{ "ime" : "Nikola Music", "grad" : [ { "ime" : "Sarajevo" } ] }
{ "ime" : "Marija Jankovic", "grad" : [ { "ime" : "Nis" } ] }
{ "ime" : "Nadja Jelicic", "grad" : [ { "ime" : "Brus" } ] }
{ "ime" : "Emilie Eric", "grad" : [ { "ime" : "Cacak" } ] }

results matching ""

    No results matching ""