diff --git a/Week1/assignment/exer1.js b/Week1/assignment/exer1.js new file mode 100644 index 000000000..653a741c7 --- /dev/null +++ b/Week1/assignment/exer1.js @@ -0,0 +1,106 @@ +const { Client } = require("pg"); + +const DB_USER = "hyfuser"; +const DB_PASSWORD = "hyfpassword"; +const DB_HOST = "localhost"; +const DB_PORT = 5432; + +const baseConfig = { + host: DB_HOST, + port: DB_PORT, + user: DB_USER, + password: DB_PASSWORD, +}; + +async function withClient(database, handler) { + const client = new Client({ ...baseConfig, database }); + await client.connect(); + + try { + return await handler(client); + } finally { + await client.end(); + } +} + +async function setupMeetup() { + try { + // 1) Drop & recreate database meetup + await withClient("postgres", async (client) => { + console.log("Recreating database meetup..."); + + await client.query("DROP DATABASE IF EXISTS meetup;"); + await client.query("CREATE DATABASE meetup;"); + }); + + + await withClient("meetup", async (db) => { + console.log("Creating tables..."); + + await db.query(` + CREATE TABLE Invitee ( + invitee_no SERIAL PRIMARY KEY, + invitee_name VARCHAR(80) NOT NULL, + invited_by VARCHAR(80) NOT NULL + ); + `); + + await db.query(` + CREATE TABLE Room ( + room_no SERIAL PRIMARY KEY, + room_name VARCHAR(80) NOT NULL, + floor_number INTEGER NOT NULL + ); + `); + + await db.query(` + CREATE TABLE Meeting ( + meeting_no SERIAL PRIMARY KEY, + meeting_title VARCHAR(120) NOT NULL, + starting_time TIMESTAMP NOT NULL, + ending_time TIMESTAMP NOT NULL, + room_no INTEGER REFERENCES Room(room_no) + ); + `); + + console.log("Inserting sample data into Invitee..."); + await db.query(` + INSERT INTO Invitee (invitee_name, invited_by) VALUES + ('Karim', 'Majd'), + ('Rim', 'Karim'), + ('Alaa', 'Karim'), + ('Ivan', 'Majd'), + ('Dima', 'Ivan'); + `); + + console.log("Inserting sample data into Room..."); + await db.query(` + INSERT INTO Room (room_name, floor_number) VALUES + ('Alfa Room', 1), + ('Atlas Room', 1), + ('Nova Room', 2), + ('Fox Room', 2), + ('Sky Room', 3); + `); + + console.log("Inserting sample data into Meeting..."); + await db.query(` + INSERT INTO Meeting (meeting_title, starting_time, ending_time, room_no) VALUES + ('Intro to Databases', '2025-11-20 09:00', '2025-11-20 10:00', 1), + ('SQL Practice', '2025-11-20 10:30', '2025-11-20 11:30', 2), + ('Node & Postgres', '2025-11-21 09:00', '2025-11-21 10:30', 3), + ('Security Session', '2025-11-21 11:00', '2025-11-21 12:00', 4), + ('Retrospective Meetup', '2025-11-22 15:00', '2025-11-22 16:00', 5); + `); + }); + + console.log("Meetup database created and filled with data."); + } catch (err) { + console.error("Error while setting up meetup database:", err.message); + } +} + +setupMeetup().catch((err) => { + console.error("Unexpected error:", err); +}); + diff --git a/Week1/assignment/exer2.js b/Week1/assignment/exer2.js new file mode 100644 index 000000000..2f78aaab5 --- /dev/null +++ b/Week1/assignment/exer2.js @@ -0,0 +1,127 @@ +const { Client } = require("pg"); + +const DB_USER = "hyfuser"; +const DB_PASSWORD = "hyfpassword"; +const DB_HOST = "localhost"; +const DB_PORT = 5432; + +const client = new Client({ + user: DB_USER, + password: DB_PASSWORD, + host: DB_HOST, + port: DB_PORT, + database: "world", +}); + +async function runQuery(label, sql) { + console.log(`\n=== ${label} ===`); + + const result = await client.query(sql); + console.table(result.rows); +} + +async function runWorldQueries() { + + try { + + await client.connect(); + console.log("Connected to world database"); + + await runQuery("1) Countries with population greater than 8 million", + ` + SELECT name, population + FROM country + WHERE population > 8000000 + ORDER BY population DESC; + `); + + await runQuery('2) Countries that have "land" in their names', + ` + SELECT name + FROM country + WHERE name ILIKE '%land%' + ORDER BY name; + `); + + await runQuery("3) Cities with population between 500,000 and 1,000,000", + ` + SELECT name, population + FROM city + WHERE population BETWEEN 500000 AND 1000000 + ORDER BY population; + `); + + await runQuery("4) Countries on the continent 'Europe'", + ` + SELECT name + FROM country + WHERE continent = 'Europe' + ORDER BY name; + `); + + await runQuery("5) Countries ordered by surface area (descending)", + ` + SELECT name, surfacearea + FROM country + ORDER BY surfacearea DESC; + `); + + await runQuery("6) Cities in the Netherlands", + ` + SELECT c.name + FROM city AS c + JOIN country AS co ON c.countrycode = co.code + WHERE co.name = 'Netherlands' + ORDER BY c.name; + `); + + await runQuery("7) Population of Rotterdam", + ` + SELECT population + FROM city + WHERE name = 'Rotterdam'; + `); + + await runQuery("8) Top 10 countries by surface area", + ` + SELECT name, surfacearea + FROM country + ORDER BY surfacearea DESC + LIMIT 10; + `); + + await runQuery( + "9) Top 10 most populated cities", + ` + SELECT name, population + FROM city + ORDER BY population DESC + LIMIT 10; + `); + + await runQuery( + "10) Total world population", + ` + SELECT SUM(population) AS world_population + FROM country; + `); + + + console.log("\n All queries executed successfully."); + + } catch (err) { + + console.error("Error while running world queries:", err.message); + + } finally { + + await client.end(); + + console.log("Connection closed."); + } +} + + +runWorldQueries().catch((err) => { + console.error("Unexpected error:", err); +}); diff --git a/Week2/assignment/db.js b/Week2/assignment/db.js new file mode 100644 index 000000000..8c881fc51 --- /dev/null +++ b/Week2/assignment/db.js @@ -0,0 +1,13 @@ +import pg from "pg"; + +const { Client } = pg; + +export function createClient() { + return new Client({ + host: process.env.DB_HOST ?? "localhost", + user: process.env.DB_USER ?? "hyfuser", + password: process.env.DB_PASSWORD ?? "hyfpassword", + database: process.env.DB_NAME ?? "research_w2", + port: Number(process.env.DB_PORT ?? 5432), + }); +} diff --git a/Week2/assignment/ex1.js b/Week2/assignment/ex1.js new file mode 100644 index 000000000..8efb6367a --- /dev/null +++ b/Week2/assignment/ex1.js @@ -0,0 +1,43 @@ +import { createClient } from "./db.js"; + +async function run() { + const client = createClient(); + + try { + await client.connect(); + + await client.query("DROP TABLE IF EXISTS authors CASCADE;"); + await client.query(` + CREATE TABLE authors ( + author_id SERIAL PRIMARY KEY, + full_name VARCHAR(120) NOT NULL, + university VARCHAR(120), + date_of_birth DATE NOT NULL, + h_index INTEGER, + gender CHAR(1) NOT NULL CHECK (gender IN ('M', 'F')) + ); + `); + + await client.query(` + ALTER TABLE authors + ADD COLUMN mentor INTEGER; + `); + + await client.query(` + ALTER TABLE authors + ADD CONSTRAINT fk_authors_mentor + FOREIGN KEY (mentor) + REFERENCES authors (author_id) + ON DELETE SET NULL; + `); + + console.log("Exercise 1: authors table + mentor FK created"); + } catch (err) { + console.error("Error in ex1-keys:", err.message); + } finally { + await client.end(); + console.log("Connection closed"); + } +} + +run(); diff --git a/Week2/assignment/ex2.js b/Week2/assignment/ex2.js new file mode 100644 index 000000000..97d53d31f --- /dev/null +++ b/Week2/assignment/ex2.js @@ -0,0 +1,143 @@ +import { createClient } from "./db.js"; + +const authorRows = [ + ["Noah Clarke", "TU Delft", "1981-04-11", 14, "M"], + ["Mila Verhoef", "TU Delft", "1986-09-02", 18, "F"], + ["Jonas Peters", "TU Eindhoven", "1979-12-23", 22, "M"], + ["Sara Koster", "TU Eindhoven", "1990-03-19", 11, "F"], + ["Liam de Bruin", "VU Amsterdam", "1984-07-08", 16, "M"], + ["Nadia Rossi", "VU Amsterdam", "1988-01-28", 19, "F"], + ["Elif Kaya", "University of Amsterdam", "1989-06-15", 13, "F"], + ["Lucas Stein", "Radboud University", "1982-11-30", 20, "M"], + ["Yara Hussain", "Radboud University", "1991-02-05", 9, "F"], + ["Tobias Meier", "ETH Zürich", "1980-08-12", 26, "M"], + ["Rosa Almeida", "ETH Zürich", "1987-05-27", 17, "F"], + ["Imran Malik", "KU Leuven", "1992-01-09", 7, "M"], + ["Emma Jansen", "KU Leuven", "1993-09-14", 5, "F"], + ["Felix Novak", "Charles University", "1985-12-03", 12, "M"], + ["Omar Haddad", "University of Amsterdam", "1977-10-04", 24, "M"], +]; + +function pad2(n) { + return String(n).padStart(2, "0"); +} + +async function seedAuthors(client) { + await client.query("TRUNCATE TABLE authors RESTART IDENTITY CASCADE;"); + + const insertSql = ` + INSERT INTO authors (full_name, university, date_of_birth, h_index, gender) + VALUES ($1, $2, $3, $4, $5); + `; + + for (const row of authorRows) { + await client.query(insertSql, row); + } + + const mentorPairs = [ + [1, 3], + [2, 3], + [4, 5], + [5, 2], + [6, 7], + [8, 1], + [9, 10], + [10, 11], + [11, 4], + [12, 6], + ]; + + for (const [authorId, mentorId] of mentorPairs) { + await client.query("UPDATE authors SET mentor = $1 WHERE author_id = $2;", [ + mentorId, + authorId, + ]); + } +} + +async function createPaperTables(client) { + await client.query("DROP TABLE IF EXISTS authors_papers;"); + await client.query("DROP TABLE IF EXISTS research_papers;"); + + await client.query(` + CREATE TABLE research_papers ( + paper_id SERIAL PRIMARY KEY, + paper_title VARCHAR(200) NOT NULL, + conference VARCHAR(100), + publish_date DATE + ); + `); + + await client.query(` + CREATE TABLE authors_papers ( + author_id INTEGER NOT NULL REFERENCES authors(author_id) ON DELETE CASCADE, + paper_id INTEGER NOT NULL REFERENCES research_papers(paper_id) ON DELETE CASCADE, + PRIMARY KEY (author_id, paper_id) + ); + `); +} + +async function seedPapersAndLinks(client) { + + for (let i = 1; i <= 30; i += 1) { + const month = pad2(((i - 1) % 9) + 1); + const year = 2020 + (i % 4); + const date = `${year}-${month}-15`; + + await client.query( + ` + INSERT INTO research_papers (paper_title, conference, publish_date) + VALUES ($1, $2, $3); + `, + [`Study on Databases ${i}`, `DataConf ${((i - 1) % 5) + 1}`, date] + ); + } + + + const links = [ + [1, 1], [2, 1], [3, 1], + [4, 2], [5, 2], + [6, 3], [7, 3], + [8, 4], [9, 4], [10, 4], + [11, 5], + [12, 6], + [13, 7], + [14, 8], + [15, 9], + [1, 10], [5, 10], + [2, 11], [6, 11], [9, 11], + [3, 12], [4, 13], [7, 14], [8, 15], [10, 16], + [11, 17], [12, 18], [13, 19], [14, 20], [15, 21], + ]; + + for (const [authorId, paperId] of links) { + await client.query( + ` + INSERT INTO authors_papers (author_id, paper_id) + VALUES ($1, $2); + `, + [authorId, paperId] + ); + } +} + +async function run() { + const client = createClient(); + + try { + await client.connect(); + + await createPaperTables(client); + await seedAuthors(client); + await seedPapersAndLinks(client); + + console.log("Exercise 2: papers + links seeded (includes multi-author papers)"); + } catch (err) { + console.error("Error in ex2:", err.message); + } finally { + await client.end(); + console.log("Connection closed"); + } +} + +run(); diff --git a/Week2/assignment/ex3.js b/Week2/assignment/ex3.js new file mode 100644 index 000000000..e68889d29 --- /dev/null +++ b/Week2/assignment/ex3.js @@ -0,0 +1,45 @@ +import { createClient } from "./db.js"; + +async function run() { + const client = createClient(); + + try { + await client.connect(); + + const q1 = ` + SELECT + a.author_id, + a.full_name AS author_name, + m.full_name AS mentor_name + FROM authors a + LEFT JOIN authors m + ON a.mentor = m.author_id + ORDER BY a.author_id; + `; + const res1 = await client.query(q1); + console.log("\nAuthors with their mentors"); + console.table(res1.rows); + + + const q2 = ` + SELECT a.*, rp.paper_title + FROM authors a + LEFT JOIN authors_papers ap + ON a.author_id = ap.author_id + LEFT JOIN research_papers rp + ON ap.paper_id = rp.paper_id + ORDER BY a.author_id, rp.paper_title; + `; + + const res2 = await client.query(q2); + console.log("\nAuthors with their paper titles (if any)"); + console.table(res2.rows); + } catch (err) { + console.error("Error in ex3-joins:", err.message); + } finally { + await client.end(); + console.log("Connection closed"); + } +} + +run(); diff --git a/Week2/assignment/ex4.js b/Week2/assignment/ex4.js new file mode 100644 index 000000000..a44c25e3b --- /dev/null +++ b/Week2/assignment/ex4.js @@ -0,0 +1,83 @@ +import pool from "./db.js"; + +async function run() { + try { + + const q1 = ` + SELECT + rp.paper_id, + rp.paper_title, + COUNT(ap.author_id) AS author_count + FROM research_papers rp + LEFT JOIN authors_papers ap + ON rp.paper_id = ap.paper_id + GROUP BY rp.paper_id, rp.paper_title + ORDER BY rp.paper_id; + `; + const res1 = await pool.query(q1); + console.log("\n1) Papers and number of authors"); + console.table(res1.rows); + + const q2 = ` + SELECT + COUNT(DISTINCT ap.paper_id) AS total_papers_by_female_authors + FROM authors a + JOIN authors_papers ap + ON a.author_id = ap.author_id + WHERE a.gender = 'F'; + `; + + const res2 = await pool.query(q2); + console.log("\n2) Total papers by female authors"); + console.table(res2.rows); + + const q3 = ` + SELECT + university, + AVG(h_index) AS average_h_index + FROM authors + GROUP BY university + ORDER BY university; + `; + + const res3 = await pool.query(q3); + console.log("\n3) Average h-index per university"); + console.table(res3.rows); + + + const q4 = ` + SELECT + a.university, + COUNT(DISTINCT ap.paper_id) AS total_papers + FROM authors a + LEFT JOIN authors_papers ap + ON a.author_id = ap.author_id + GROUP BY a.university + ORDER BY a.university; + `; + const res4 = await pool.query(q4); + console.log("\n4) Total papers per university"); + console.table(res4.rows); + + const q5 = ` + SELECT + university, + MIN(h_index) AS min_h_index, + MAX(h_index) AS max_h_index + FROM authors + GROUP BY university + ORDER BY university; + `; + + const res5 = await pool.query(q5); + console.log("\n5) Min/Max h-index per university"); + console.table(res5.rows); + } catch (err) { + console.error("Error in ex4-aggregate-functions:", err.message); + } finally { + await pool.end(); + console.log("Connection closed"); + } +} + +run();