From f45ec50b88a41c8117460e3827b9c34ddfdcffda Mon Sep 17 00:00:00 2001 From: VLADISLAV NACHIKOV Date: Wed, 3 Sep 2025 22:56:01 +0200 Subject: [PATCH 1/2] 2week as --- Week2/scripts/ex3_1_keys.js | 92 +++++++++++++++++++ Week2/scripts/ex3_2_relationships.js | 126 +++++++++++++++++++++++++++ Week2/scripts/ex3_3_joins.js | 55 ++++++++++++ Week2/scripts/ex3_4_aggregates.js | 88 +++++++++++++++++++ Week2/scripts/package.json | 11 ++- 5 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 Week2/scripts/ex3_1_keys.js create mode 100644 Week2/scripts/ex3_2_relationships.js create mode 100644 Week2/scripts/ex3_3_joins.js create mode 100644 Week2/scripts/ex3_4_aggregates.js diff --git a/Week2/scripts/ex3_1_keys.js b/Week2/scripts/ex3_1_keys.js new file mode 100644 index 000000000..51c559ad8 --- /dev/null +++ b/Week2/scripts/ex3_1_keys.js @@ -0,0 +1,92 @@ +// ex3_1_keys.js +// Week2 – Exercise 3.1 (Keys) +// Creates DB "research", table authors, and adds self-referencing FK "mentor". + +import dotenv from "dotenv"; +import pg from "pg"; + +dotenv.config(); +const { Client } = pg; + +const DB_NAME = "research"; + +const admin = new Client({ + host: process.env.PGHOST, + port: process.env.PGPORT, + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + database: "postgres", +}); + +const research = (dbName = DB_NAME) => + new Client({ + host: process.env.PGHOST, + port: process.env.PGPORT, + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + database: dbName, + }); + +async function run() { + let root, db; + try { + // 1) Create DB "research" fresh (idempotent) + root = admin; + await root.connect(); + await root.query( + `SELECT pg_terminate_backend(pid) + FROM pg_stat_activity + WHERE datname = $1 AND pid <> pg_backend_pid();`, + [DB_NAME] + ); + await root.query(`DROP DATABASE IF EXISTS ${DB_NAME};`); + await root.query(`CREATE DATABASE ${DB_NAME};`); + await root.end(); + root = null; + + // 2) Connect to research and create authors + db = research(); + await db.connect(); + + await db.query(` + CREATE TABLE authors ( + author_id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + author_name TEXT NOT NULL, + university TEXT NOT NULL, + date_of_birth DATE, + h_index SMALLINT CHECK (h_index >= 0), + gender TEXT CHECK (gender IN ('male','female','nonbinary','other')) + ); + `); + + // 3) Add mentor column that references authors.author_id (self FK) + await db.query(` + ALTER TABLE authors + ADD COLUMN mentor INTEGER NULL; + `); + + await db.query(` + ALTER TABLE authors + ADD CONSTRAINT fk_authors_mentor + FOREIGN KEY (mentor) REFERENCES authors(author_id) + ON DELETE SET NULL; + `); + + console.log(" ex3_1_keys: authors created, mentor FK added"); + } catch (e) { + console.error(" ex3_1_keys failed:", e); + process.exitCode = 1; + } finally { + if (root) { + try { + await root.end(); + } catch {} + } + if (db) { + try { + await db.end(); + } catch {} + } + } +} +run(); diff --git a/Week2/scripts/ex3_2_relationships.js b/Week2/scripts/ex3_2_relationships.js new file mode 100644 index 000000000..26bb6871c --- /dev/null +++ b/Week2/scripts/ex3_2_relationships.js @@ -0,0 +1,126 @@ +// ex3_2_relationships.js +// Week2 – Exercise 3.2 (Relationships) +// Adds research_papers, author_papers; inserts 15 authors, 30 papers, and links. + +require("dotenv").config(); +const { Client } = require("pg"); + +const DB_NAME = "research"; +const db = new Client({ + host: process.env.PGHOST, + port: process.env.PGPORT, + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + database: DB_NAME, +}); + +async function run() { + try { + await db.connect(); + + // 1) Tables for papers and m:n link + await db.query(` + CREATE TABLE IF NOT EXISTS research_papers ( + paper_id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + paper_title TEXT NOT NULL UNIQUE, + conference TEXT, + publish_date DATE + ); + `); + + await db.query(` + CREATE TABLE IF NOT EXISTS author_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) + ); + `); + + // 2) Seed 15 authors (some mentors set later) + const authors = [ + ["Alice Kim", "TU Delft", "1988-03-01", 12, "female"], + ["Bob Ivanov", "UvA", "1985-06-10", 18, "male"], + ["Carla Gomez", "VU", "1990-12-01", 9, "female"], + ["Dmitri Petrov", "TU Delft", "1982-07-22", 22, "male"], + ["Eve Tan", "Leiden", "1991-09-19", 7, "female"], + ["Felix Zhou", "TU/e", "1986-01-05", 15, "male"], + ["Gina Rossi", "UvA", "1992-04-14", 6, "female"], + ["Hiro Sato", "TU Delft", "1984-11-08", 24, "male"], + ["Ivy Wang", "Leiden", "1993-02-17", 5, "female"], + ["Jamal Noor", "VU", "1987-08-03", 11, "male"], + ["Kira Novak", "UvA", "1990-01-29", 8, "female"], + ["Leo Martins", "TU/e", "1983-03-13", 19, "male"], + ["Mina Park", "Leiden", "1994-04-04", 4, "female"], + ["Nate Quinn", "TU Delft", "1989-06-06", 13, "male"], + ["Ola Berg", "VU", "1985-10-10", 17, "male"], + ]; + + await db.query(`TRUNCATE authors RESTART IDENTITY CASCADE;`); + for (const a of authors) { + await db.query( + `INSERT INTO authors(author_name,university,date_of_birth,h_index,gender) + VALUES ($1,$2,$3,$4,$5);`, + a + ); + } + + // наставники: сделаем 4х менторов для примера + await db.query( + `UPDATE authors SET mentor = 4 WHERE author_id IN (1,3,5);` + ); + await db.query( + `UPDATE authors SET mentor = 8 WHERE author_id IN (2,6,7);` + ); + await db.query( + `UPDATE authors SET mentor = 12 WHERE author_id IN (9,10,11);` + ); + await db.query( + `UPDATE authors SET mentor = 4 WHERE author_id IN (13,14,15);` + ); + + // 3) Seed 30 papers (генерируем названия) + await db.query(`TRUNCATE research_papers RESTART IDENTITY CASCADE;`); + const confs = ["ICDB", "SIGMOD", "VLDB", "ICDE", "EDBT"]; + const today = new Date(); + + for (let i = 1; i <= 30; i++) { + const title = `On Relational Patterns ${i}`; + const conf = confs[i % confs.length]; + const date = new Date(today.getFullYear() - (i % 8), i % 12, (i % 28) + 1) + .toISOString() + .slice(0, 10); + await db.query( + `INSERT INTO research_papers(paper_title,conference,publish_date) + VALUES ($1,$2,$3);`, + [title, conf, date] + ); + } + + // 4) Link authors to papers (каждая статья с 1–3 авторами) + await db.query(`TRUNCATE author_papers;`); + for (let paperId = 1; paperId <= 30; paperId++) { + // выберем 1–3 авторов по простому правилу + const a1 = ((paperId + 0) % 15) + 1; + const a2 = ((paperId + 3) % 15) + 1; + const a3 = ((paperId + 7) % 15) + 1; + const pick = new Set([a1, a2, ...(paperId % 3 === 0 ? [a3] : [])]); + for (const aid of pick) { + await db.query( + `INSERT INTO author_papers(author_id,paper_id) + VALUES ($1,$2) ON CONFLICT DO NOTHING;`, + [aid, paperId] + ); + } + } + + console.log(" ex3_2_relationships: papers, links, and seed inserted"); + } catch (e) { + console.error(" ex3_2_relationships failed:", e); + process.exitCode = 1; + } finally { + try { + await db.end(); + } catch {} + } +} +run(); diff --git a/Week2/scripts/ex3_3_joins.js b/Week2/scripts/ex3_3_joins.js new file mode 100644 index 000000000..83094fd4f --- /dev/null +++ b/Week2/scripts/ex3_3_joins.js @@ -0,0 +1,55 @@ +// ex3_3_joins.js +// Week2 – Exercise 3.3 (Joins) + +require("dotenv").config(); +const { Client } = require("pg"); + +const db = new Client({ + host: process.env.PGHOST, + port: process.env.PGPORT, + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + database: "research", +}); + +async function q(label, sql, params = []) { + const res = await db.query(sql, params); + console.log(`\n=== ${label} ===`); + console.table(res.rows.slice(0, 15)); +} + +async function run() { + try { + await db.connect(); + + // 1) names of all authors and their mentors + await q( + "Authors and their mentors", + `SELECT a.author_name AS author, m.author_name AS mentor + FROM authors a + LEFT JOIN authors m ON a.mentor = m.author_id + ORDER BY author;` + ); + + // 2) all columns of authors and their published paper_title + // include authors without papers + await q( + "Authors and their papers (include authors without papers)", + `SELECT a.*, rp.paper_title + FROM authors a + LEFT JOIN author_papers ap ON ap.author_id = a.author_id + LEFT JOIN research_papers rp ON rp.paper_id = ap.paper_id + ORDER BY a.author_id, rp.paper_title NULLS LAST;` + ); + + console.log("\n ex3_3_joins: done"); + } catch (e) { + console.error(" ex3_3_joins failed:", e); + process.exitCode = 1; + } finally { + try { + await db.end(); + } catch {} + } +} +run(); diff --git a/Week2/scripts/ex3_4_aggregates.js b/Week2/scripts/ex3_4_aggregates.js new file mode 100644 index 000000000..49d55f997 --- /dev/null +++ b/Week2/scripts/ex3_4_aggregates.js @@ -0,0 +1,88 @@ +// ex3_4_aggregates.js +// Week2 – Exercise 3.4 (Aggregate Functions) + +require("dotenv").config(); +const { Client } = require("pg"); + +const db = new Client({ + host: process.env.PGHOST, + port: process.env.PGPORT, + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + database: "research", +}); + +async function q(label, sql, params = []) { + const res = await db.query(sql, params); + console.log(`\n=== ${label} ===`); + console.table(res.rows.slice(0, 20)); +} + +async function run() { + try { + await db.connect(); + + // 1) All research papers and the number of authors that wrote that paper + await q( + "Paper → number of authors", + `SELECT rp.paper_title, COUNT(ap.author_id) AS authors_count + FROM research_papers rp + LEFT JOIN author_papers ap ON ap.paper_id = rp.paper_id + GROUP BY rp.paper_id, rp.paper_title + ORDER BY authors_count DESC, rp.paper_title;` + ); + + // 2) Sum of the research papers published by all female authors + await q( + "Total papers by female authors", + `SELECT SUM(cnt) AS total_papers_by_female + FROM ( + SELECT a.author_id, COUNT(ap.paper_id) AS cnt + FROM authors a + LEFT JOIN author_papers ap ON ap.author_id = a.author_id + WHERE a.gender = 'female' + GROUP BY a.author_id + ) x;` + ); + + // 3) Average of the h-index of all authors per university + await q( + "Average h-index per university", + `SELECT university, ROUND(AVG(h_index)::numeric, 2) AS avg_h_index + FROM authors + GROUP BY university + ORDER BY university;` + ); + + // 4) Sum of the research papers of the authors per university + await q( + "Sum of papers per university", + `SELECT a.university, COUNT(ap.paper_id) AS papers_sum + FROM authors a + LEFT JOIN author_papers ap ON ap.author_id = a.author_id + GROUP BY a.university + ORDER BY papers_sum DESC, a.university;` + ); + + // 5) Min/Max h-index per university + await q( + "Min/Max h-index per university", + `SELECT university, + MIN(h_index) AS min_h, + MAX(h_index) AS max_h + FROM authors + GROUP BY university + ORDER BY university;` + ); + + console.log("\n ex3_4_aggregates: done"); + } catch (e) { + console.error(" ex3_4_aggregates failed:", e); + process.exitCode = 1; + } finally { + try { + await db.end(); + } catch {} + } +} +run(); diff --git a/Week2/scripts/package.json b/Week2/scripts/package.json index 5f08bd668..8be91403d 100644 --- a/Week2/scripts/package.json +++ b/Week2/scripts/package.json @@ -5,5 +5,14 @@ }, "devDependencies": { "@types/pg": "^8.10.9" - } + }, + "name": "scripts", + "version": "1.0.0", + "main": "async-create-insert.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "description": "" } From d32ab931ca2910a5e70fba7693574caa6bbc41d9 Mon Sep 17 00:00:00 2001 From: VLADISLAV NACHIKOV Date: Wed, 3 Sep 2025 23:09:57 +0200 Subject: [PATCH 2/2] Changes all req to import + small fixes --- Week2/scripts/ex3_1_keys.js | 2 +- Week2/scripts/ex3_2_relationships.js | 13 +++++++------ Week2/scripts/ex3_3_joins.js | 7 +++++-- Week2/scripts/ex3_4_aggregates.js | 7 +++++-- Week2/scripts/package-lock.json | 19 ++++++++++++++++++- Week2/scripts/package.json | 3 ++- 6 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Week2/scripts/ex3_1_keys.js b/Week2/scripts/ex3_1_keys.js index 51c559ad8..27eec39d7 100644 --- a/Week2/scripts/ex3_1_keys.js +++ b/Week2/scripts/ex3_1_keys.js @@ -4,8 +4,8 @@ import dotenv from "dotenv"; import pg from "pg"; - dotenv.config(); + const { Client } = pg; const DB_NAME = "research"; diff --git a/Week2/scripts/ex3_2_relationships.js b/Week2/scripts/ex3_2_relationships.js index 26bb6871c..f7872be49 100644 --- a/Week2/scripts/ex3_2_relationships.js +++ b/Week2/scripts/ex3_2_relationships.js @@ -2,8 +2,11 @@ // Week2 – Exercise 3.2 (Relationships) // Adds research_papers, author_papers; inserts 15 authors, 30 papers, and links. -require("dotenv").config(); -const { Client } = require("pg"); +import dotenv from "dotenv"; +import pg from "pg"; +dotenv.config(); + +const { Client } = pg; const DB_NAME = "research"; const db = new Client({ @@ -64,7 +67,6 @@ async function run() { ); } - // наставники: сделаем 4х менторов для примера await db.query( `UPDATE authors SET mentor = 4 WHERE author_id IN (1,3,5);` ); @@ -78,7 +80,7 @@ async function run() { `UPDATE authors SET mentor = 4 WHERE author_id IN (13,14,15);` ); - // 3) Seed 30 papers (генерируем названия) + // 3) Seed 30 papers await db.query(`TRUNCATE research_papers RESTART IDENTITY CASCADE;`); const confs = ["ICDB", "SIGMOD", "VLDB", "ICDE", "EDBT"]; const today = new Date(); @@ -96,10 +98,9 @@ async function run() { ); } - // 4) Link authors to papers (каждая статья с 1–3 авторами) + // 4) Link authors to papers await db.query(`TRUNCATE author_papers;`); for (let paperId = 1; paperId <= 30; paperId++) { - // выберем 1–3 авторов по простому правилу const a1 = ((paperId + 0) % 15) + 1; const a2 = ((paperId + 3) % 15) + 1; const a3 = ((paperId + 7) % 15) + 1; diff --git a/Week2/scripts/ex3_3_joins.js b/Week2/scripts/ex3_3_joins.js index 83094fd4f..7816195c2 100644 --- a/Week2/scripts/ex3_3_joins.js +++ b/Week2/scripts/ex3_3_joins.js @@ -1,8 +1,11 @@ // ex3_3_joins.js // Week2 – Exercise 3.3 (Joins) -require("dotenv").config(); -const { Client } = require("pg"); +import dotenv from "dotenv"; +import pg from "pg"; +dotenv.config(); + +const { Client } = pg; const db = new Client({ host: process.env.PGHOST, diff --git a/Week2/scripts/ex3_4_aggregates.js b/Week2/scripts/ex3_4_aggregates.js index 49d55f997..c144ae6d0 100644 --- a/Week2/scripts/ex3_4_aggregates.js +++ b/Week2/scripts/ex3_4_aggregates.js @@ -1,8 +1,11 @@ // ex3_4_aggregates.js // Week2 – Exercise 3.4 (Aggregate Functions) -require("dotenv").config(); -const { Client } = require("pg"); +import dotenv from "dotenv"; +import pg from "pg"; +dotenv.config(); + +const { Client } = pg; const db = new Client({ host: process.env.PGHOST, diff --git a/Week2/scripts/package-lock.json b/Week2/scripts/package-lock.json index 64787b766..2c4ceb969 100644 --- a/Week2/scripts/package-lock.json +++ b/Week2/scripts/package-lock.json @@ -1,11 +1,16 @@ { "name": "scripts", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { + "name": "scripts", + "version": "1.0.0", + "license": "ISC", "dependencies": { - "pg": "^8.11.3" + "dotenv": "^17.2.2", + "pg": "^8.16.3" }, "devDependencies": { "@types/pg": "^8.10.9" @@ -33,6 +38,18 @@ "pg-types": "^2.2.0" } }, + "node_modules/dotenv": { + "version": "17.2.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz", + "integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/pg": { "version": "8.16.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", diff --git a/Week2/scripts/package.json b/Week2/scripts/package.json index 8be91403d..c8f17c301 100644 --- a/Week2/scripts/package.json +++ b/Week2/scripts/package.json @@ -1,7 +1,8 @@ { "type": "module", "dependencies": { - "pg": "^8.11.3" + "dotenv": "^17.2.2", + "pg": "^8.16.3" }, "devDependencies": { "@types/pg": "^8.10.9"