Edit this page Backlinks This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== Tema 2 PP 2023 ====== <note important> **DEADLINE '' TODO '' ** * Temele trebuie trimise pe curs.upb.ro, în assignment-ul numit ''Tema 2''. * Temele ce nu sunt acceptate de validatorul de arhive **NU** vor fi punctate. * Vă sugerăm ca, inainte de trimitere, sa testați arhiva voastra cu validatorul de arhive. </note> **TODO**: ar mai trebui sa avem partea de inserare, stergere avand in vedere ca o sa avem doar query-uri de tip SELECT si JOIN? In aceasta tema veti implementa un **query engine** minimal, care operează pe tabele, pentru a facilita selectarea, inserarea, stergerea datelor existente dintr-un **tabel**. **TODO**: as propune urmatoarea descriere: În această temă veți implementa un **query engine** minimal, care operează pe **tabele**, pentru a facilita filtrarea(**SELECT**) si alăturarea lor(**JOIN**). Având aceste funcționalități, veti putea implementa **funcții statistice**, care să lucreze pe acestea. ==== Ce este un tabel? ==== Formatul unui tabel pentru acesta tema va fi CSV. De exemplu, pentru tabela **people**, un exemplu de conținut ar fi următorul: <code csv> id,name,age,sex,positionId 1,Turing,43,M,2xf 2,Curry,27,M,9cc 3,Radia,29,F,2xf, 4,Chomsky,94,M,7af </code> Pe prima linie sunt definite denumirile coloanelor (id,name,age,sex,positionId). Pe următoarele linii se află intrările din tabelă. Pentru a putea face operații pe o tabelă, aceasta va trebui sa aibe o reprezentare **internă** care să ajute acest fapt. O soluție simplă este ca aceasta să fie transformată dintr-un simplu String **CSV** într-o **matrice** de String-uri, de următorul tip: <code scala> type Table = List[List[String]] </code> ==== Tipuri de query-uri ==== * **SELECT**: are rolul de a filtra o tabelă pe baza unei condiții date legată de o coloană, precum si alegerea coloanelor care vor participa în rezultat. * **JOIN**: combină înregistrări din două tabele pe baza unor coloane comune. Cu ajutorul lor se vor putea implementa **funcții statistice**, cu ajutorul cărora se pot număra linii specifice din tabelă, se poate face media unui atribut etc. ==== Task 1: SELECT query ==== În prima etapă, va trebui implementată funcționalitate de **SELECT**. Cererea de tip **SELECT** lucrează pe o singură tabelă, având rolul de a filtra linile din aceasta, precum și de a alege ce coloane se vor afla în rezultat. <hidden Mai multe explicații> Pentru a avea o înțelegere mai bună a acesteia, urmează un exemplu din **SQL*plus**: <code sql> SELECT name, age FROM people WHERE age < 30; </code> * Pe prima linie este clauza //SELECT//, unde se specifică coloanele care se vor afla în rezultat. * Pe a doua linie este clauza //FROM//, unde se află tabela pe care va avea loc operația descrisă. * Pe ultima linie se află clauza //WHERE//, urmată de o condiție de filtrare a linilor tabelei **people**. În concluzie, rezultatul va fi o nouă tabelă, formată doar din persoanele mai tinere de 30 ani, având ca informații numele și vârsta. </hidden> Acesta va avea următoarea signatură: <code scala> def select(table: Table, columnsToExtract: List[String], columnToCompare: String, condition: (String => Boolean)): Table </code> Fiecare parametru are următoarea semnificație: * **table**: tabela primită, parsată din formatul CSV * **columnsToExtract**: lista cu denumirea coloanelor care vor fi prezente în rezultat * **columnToCompare**: coloana pe baza căreia va trebuie sa se facă filtrarea * **condition**: funcția de filtrare a tabelei Valoarea de **return** este tot o tabelă parsată, rezultată din filtrarea si proiecția celei primite ca input. ** Exemplu: ** <code scala> // pentru acest exemplu se va folosi // tabela din prima secțiune (people) select(people, List("name", "age"), "age", (age: String => age.toInt < 30)) </code> Rezultatul va trebui să fie tot o tabelă parsată, având linile din **people** cu coloana **age** mai mică decât 30. De asemenea, se vor păstra doar coloanele **name** și **age**: Reprezentarea rezultatului în format CSV: <code csv> name,age Curry,27 Radia,29 </code> ==== Task 2: JOIN query ==== <code scala> def join(table1: Table, table2: Table, column1: String, column2: String): Table </code> Query-ul **join** combină două tabele, alăturând coloanele din acestea, be baza egalității între coloanele cu semnificație comună, specificate în cerere. Exemplu: <code scala> // Row trebuie implementat sau definit în funcție de alte tipuri concrete def joinQuery(query: String): List[(Row, List[Row])] val result = joinQuery("JOIN people, ratings ON id, revieweeId") // [ // (Row(1, Turing, 43, M, 2xf), [Row(1, 5, 1, 9.2, 0.87), Row(4, 4, 1, 6.3, 0.53)]), // (Row(2, Curry, 27, M, 9cc), [Row(3, 5, 2, 10.0, 1.0)]), // (Row(3, Radia, 9, F, 2xf), [Row(2, 5, 3, 9.7, 0.92), Row(5, 4, 3, 4.1, 0.26)]), // (Row(4, Chomsky, 94, M, 7af), []), // ] </code> ==== Task 3: Statistici ==== În această etapă, vom folosi etapele anterioare pentru realizarea de statistici pentru datele existente. Statisticile vor reprezenta, în mare, o singură valoare care corespunde răspunsului unei cerințe. **Exemplu**: ''Care este media vârstei persoanelor din departamentul IT?'' Funcția asociată acestei întrebări trebuie să se folosească de etapele anterioare: * folosirea unui join query pentru a prelua persoanele din fiecare departament, * urmată de o procesare a listei rezultat pentru a returna doar oamenii din departamanetul ''IT'', * urmată de o procesasre pentru calcularea mediei vârstei. **TODO**: schimbare pseudocod astfel incat sa se conformeze la noua signatura a functilor <hidden> pseudocod pentru realizare: <code scala> val join = joinQuery("JOIN positions, persons ON id, positionId") val itPeople = join.filter( x => x._1 == "IT" ).head._2 val sumAge = itPeople.map(x => x.age).sum return sumAge / itPeople.size </code> </hidden> **Listă statistici de implementat**: - Care este **media** vârstei persoanelor de pe //<department_name>//? - Care este vârsta **medie** a persoanelor care au terminat, în medie, mai mult de //<percentage>// din task-uri? - **Câți** bărbați căștigă mai mult de //<monthly_salary>// pe lună? - Care este **raportul** dintre numărul de femei și numărul de bărbați care lucrează în cadrul departamentului de //<department_name>//? - Care este bonusul anual **mediu** al persoanelor mai tinere de //<age>// ani? ==== Alte informații ==== <note warning> **Folosiți un stil de programare funcțional. NU se vor accepta:** * **Efecte laterale** (de exemplu modificarea parametrilor dați ca input la funcție) * Folosirea colecților aflate în **scala.collection.mutable** * **var** (**val** este ok!) </note> <note>Scheletul se poate găsi la: {{'' TODO ''}}</note> <note> Validatorul de arhive se poate găsi la: {{ ''TODO''}} \\ Formatul arhivelor este: * '' TODO '' * ID.txt - acest fișier va conține o singură linie, formată din ID-ul unic al fiecărui student Numele arhivelor trebuie să fie de forma **<Nume>_<Prenume>_<Grupa>_T2.zip** (daca aveti mai multe prenume sau nume, le puteți separa prin '-') </note>