From 7110e3c714680dbfe4f4a852f1516420a583ce56 Mon Sep 17 00:00:00 2001 From: Hanna Date: Tue, 6 Aug 2024 22:38:13 +0200 Subject: [PATCH 1/5] Assignment databases w1 --- Week1/.gitignore | 5 ++- Week1/exercise_1.js | 76 +++++++++++++++++++++++++++++++++++++++++++++ Week1/exercise_2.js | 44 ++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 Week1/exercise_1.js create mode 100644 Week1/exercise_2.js diff --git a/Week1/.gitignore b/Week1/.gitignore index 7df420767..b756fed36 100644 --- a/Week1/.gitignore +++ b/Week1/.gitignore @@ -1,3 +1,6 @@ package.json package-lock.json -create-table.js \ No newline at end of file +create-table.js + + +Week1/node_modules/ \ No newline at end of file diff --git a/Week1/exercise_1.js b/Week1/exercise_1.js new file mode 100644 index 000000000..965a3ca5d --- /dev/null +++ b/Week1/exercise_1.js @@ -0,0 +1,76 @@ +const mysql = require('mysql'); + +const connection = mysql.createConnection({ + host: 'localhost', + user: 'hyfuser', + password: 'hyfpassword', + multipleStatements: true +}); + + +/*Connect to SQL server*/ +connection.connect(err => { + if (err) { + return console.error('Connection error: ' + err.stack); + } + console.log('Connected!'); +}); + + +/*SQL queries*/ +const createDatabaseAndTables = + `DROP DATABASE IF EXISTS meetup; + CREATE DATABASE meetup; + USE meetup; + + CREATE TABLE Invitee ( + invitee_no INT AUTO_INCREMENT PRIMARY KEY, + invitee_name VARCHAR(100), + invited_by VARCHAR(100) + ); + + CREATE TABLE Room ( + room_no INT AUTO_INCREMENT PRIMARY KEY, + room_name VARCHAR(50), + floor_number INT + ); + + CREATE TABLE Meeting ( + meeting_no INT AUTO_INCREMENT PRIMARY KEY, + meeting_title VARCHAR(100), + starting_time DATETIME, + ending_time DATETIME, + room_no INT, + FOREIGN KEY (room_no) REFERENCES Room(room_no) + ); + + INSERT INTO Room (room_name, floor_number) VALUES + ('Paris', 1), + ('New York', 2), + ('Tokyo', 3), + ('London', 4), + ('Berlin', 5); + + INSERT INTO Invitee (invitee_name, invited_by) VALUES + ('Victor Hugo', 'Alexandre Dumas'), + ('Mark Twain', 'Henry James'), + ('Haruki Murakami', 'Kenzaburo Oe'), + ('Charles Dickens', 'Wilkie Collins'), + ('Albert Einstein', 'Niels Bohr'); + + INSERT INTO Meeting (meeting_title, starting_time, ending_time, room_no) VALUES + ('Literary Classics Discussion', '2024-08-01 09:00:00', '2024-08-01 10:00:00', 1), + ('American Literature Seminar', '2024-08-02 11:00:00', '2024-08-02 12:00:00', 2), + ('Japanese Fiction Workshop', '2024-08-03 14:00:00', '2024-08-03 15:00:00', 3), + ('Victorian Literature Symposium', '2024-08-04 16:00:00', '2024-08-04 17:00:00', 4), + ('Scientific Innovations Forum', '2024-08-05 13:00:00', '2024-08-05 14:00:00', 5);`; + + +// Execute the queries +connection.query(createDatabaseAndTables, (error, results, fields) => { + if (error) throw error; + console.log('Database and tables created, and data inserted'); +}); + +// Close the connection +connection.end(); \ No newline at end of file diff --git a/Week1/exercise_2.js b/Week1/exercise_2.js new file mode 100644 index 000000000..f4b24fecf --- /dev/null +++ b/Week1/exercise_2.js @@ -0,0 +1,44 @@ +const mysql = require('mysql'); + +const connection = mysql.createConnection({ + host: 'localhost', + user: 'hyfuser', + password: 'hyfpassword', + database: 'world' +}); + +/*Connect to SQL server*/ +connection.connect(err => { + if (err) { + return console.error('Connection error: ' + err.stack); + } + console.log('Connected!'); +}); + + +// Queries +const queries = [ + "SELECT Name FROM country WHERE Population > 8000000;", + "SELECT Name FROM country WHERE Name LIKE '%land%';", + "SELECT Name FROM city WHERE Population BETWEEN 500000 AND 1000000;", + "SELECT Name FROM country WHERE Continent = 'Europe';", + "SELECT Name FROM country ORDER BY SurfaceArea DESC;", + "SELECT Name FROM city WHERE CountryCode = 'NLD';", + "SELECT Population FROM city WHERE Name = 'Rotterdam';", + "SELECT Name FROM country ORDER BY SurfaceArea DESC LIMIT 10;", + "SELECT Name FROM city ORDER BY Population DESC LIMIT 10;", + "SELECT SUM(Population) AS WorldPopulation FROM country;" +]; + + +// Execute each query +queries.forEach((query, index) => { + connection.query(query, (error, results) => { + if (error) throw error; + console.log(`Query ${index + 1}:`); + console.log(results); + }); +}); + +// End the connection +connection.end(); \ No newline at end of file From 208d38626a970168f64e8daae00ca82489513950 Mon Sep 17 00:00:00 2001 From: Hanna Date: Sun, 11 Aug 2024 23:12:07 +0200 Subject: [PATCH 2/5] Assignment databases w2 completed --- .gitignore | 1 + Week2/connection_query.js | 29 ++++++ Week2/exercise_1.js | 99 +++++++++++++++++++ Week2/exercise_2.js | 202 ++++++++++++++++++++++++++++++++++++++ Week2/exercise_3.js | 81 +++++++++++++++ Week2/exercise_4.js | 164 +++++++++++++++++++++++++++++++ 6 files changed, 576 insertions(+) create mode 100644 Week2/connection_query.js create mode 100644 Week2/exercise_1.js create mode 100644 Week2/exercise_2.js create mode 100644 Week2/exercise_3.js create mode 100644 Week2/exercise_4.js diff --git a/.gitignore b/.gitignore index 2f49144f0..42212417c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules/ +*/node_modules */node_modules/ **/*.DS_Store **/*-secret.json diff --git a/Week2/connection_query.js b/Week2/connection_query.js new file mode 100644 index 000000000..60e0cdc7b --- /dev/null +++ b/Week2/connection_query.js @@ -0,0 +1,29 @@ +//C:\Users\knowl\Documents\hyf\databases\Week2\connection_query.js +import mysql from 'mysql'; + +export const createNewConnection = () => { + return mysql.createConnection({ + host: 'localhost', + user: 'hyfuser', + password: 'hyfpassword', + multipleStatements: true, + }); +}; + +export const useDatabase = (connection) => { + return new Promise((resolve, reject) => { + const createDatabaseAndUse = ` + CREATE DATABASE IF NOT EXISTS w2_research; + USE w2_research; + `; + connection.query(createDatabaseAndUse, (err, results) => { + if (err) { + console.error('Error creating or selecting database:', err.stack); + reject(err); + return; + } + console.log('Database selected successfully.'); + resolve(); + }); + }); +}; \ No newline at end of file diff --git a/Week2/exercise_1.js b/Week2/exercise_1.js new file mode 100644 index 000000000..b5f052265 --- /dev/null +++ b/Week2/exercise_1.js @@ -0,0 +1,99 @@ +//C:\Users\knowl\Documents\hyf\databases\Week2\exercise_1.js +import {createNewConnection, useDatabase} from './connection_query.js'; + + +const createAuthorsTable = (connection) => { + return new Promise((resolve, reject) => { + const createAuthorsTableQuery = ` + CREATE TABLE IF NOT EXISTS authors ( + author_id INT AUTO_INCREMENT PRIMARY KEY, + author_name VARCHAR(100) NOT NULL, + university VARCHAR(100), + date_of_birth DATE, + h_index INT, + gender ENUM('Male', 'Female', 'Other') + ); + `; + connection.query(createAuthorsTableQuery, (err, results) => { + if (err) { + console.error('Error creating authors table:', err.stack); + reject(err); + return; + } + console.log('Authors table created.'); + resolve(); + }); + }); +}; + +const addMentorColumn = (connection) => { + return new Promise((resolve, reject) => { + const checkColumnExistsQuery = ` + SELECT COUNT(*) AS columnExists + FROM information_schema.columns + WHERE table_name = 'authors' + AND column_name = 'mentor'; + `; + + connection.query(checkColumnExistsQuery, (err, results) => { + if (err) { + console.error('Error checking for mentor column:', err.stack); + reject(err); + return; + } + + const columnExists = results[0].columnExists; + + if (columnExists) { + console.log('Mentor column already exists. No changes made.'); + resolve(); + } else { + const addMentorColumnQuery = ` + ALTER TABLE authors + ADD COLUMN mentor INT, + ADD CONSTRAINT fk_mentor + FOREIGN KEY (mentor) REFERENCES authors(author_id) + ON DELETE SET NULL + ON UPDATE CASCADE; + `; + + connection.query(addMentorColumnQuery, (err, results) => { + if (err) { + console.error('Error adding mentor column:', err.stack); + reject(err); + return; + } + console.log('Mentor column added with foreign key constraint.'); + resolve(); + }); + } + }); + }); +}; + + +const exerciseOne = async () => { + const connection = createNewConnection(); + connection.connect(err => { + if (err) { + return console.error('Connection error: ' + err.stack); + } + console.log('exercise_1: Connected!'); + }); + + + try { + await useDatabase(connection) ; // Select the database + await createAuthorsTable(connection) ; // Create the authors table + await addMentorColumn(connection) ; // Add the mentor column with foreign key + + } catch (err) { + console.error('Failed to set up the database:', err); + } finally { + connection.end(); + } +}; + + +exerciseOne(); + diff --git a/Week2/exercise_2.js b/Week2/exercise_2.js new file mode 100644 index 000000000..18d1168b1 --- /dev/null +++ b/Week2/exercise_2.js @@ -0,0 +1,202 @@ +//C:\Users\knowl\Documents\hyf\databases\Week2\exercise_2.js +import {createNewConnection, useDatabase} from './connection_query.js'; + + +const createResearchPapersTable = async (connection) => { + const createResearchPapersTableQuery = ` + CREATE TABLE IF NOT EXISTS research_papers ( + paper_id INT AUTO_INCREMENT PRIMARY KEY, + paper_title VARCHAR(255) NOT NULL, + conference VARCHAR(255), + publish_date DATE + ); + `; + await new Promise((resolve, reject) => { + connection.query(createResearchPapersTableQuery, (err, results) => { + if (err) { + console.error('Error creating research_papers table:', err.stack); + reject(err); + return; + } + console.log('Research papers table created.'); + resolve(); + }); + }); +}; + +const createAuthorPapersTable = async (connection) => { + const createAuthorPapersTableQuery = ` + CREATE TABLE IF NOT EXISTS author_papers ( + author_id INT, + paper_id INT, + PRIMARY KEY (author_id, paper_id), + FOREIGN KEY (author_id) REFERENCES authors(author_id) + ON DELETE CASCADE + ON UPDATE CASCADE, + FOREIGN KEY (paper_id) REFERENCES research_papers(paper_id) + ON DELETE CASCADE + ON UPDATE CASCADE + ); + `; + await new Promise((resolve, reject) => { + connection.query(createAuthorPapersTableQuery, (err, results) => { + if (err) { + console.error('Error creating author_papers table:', err.stack); + reject(err); + return; + } + console.log('Author papers table created.'); + resolve(); + }); + }); +}; + + +const authors = [ + ['Alan Turing', 'University of Cambridge', '1912-06-23', 67, 'Male', null], + ['Ada Lovelace', 'University of London', '1815-12-10', 45, 'Female', null], + ['John von Neumann', 'Princeton University', '1903-12-28', 86, 'Male', null], + ['Grace Hopper', 'Yale University', '1906-12-09', 60, 'Female', 1], + ['Donald Knuth', 'Stanford University', '1938-01-10', 127, 'Male', 3], + ['Edsger Dijkstra', 'University of Texas', '1930-05-11', 87, 'Male', null], + ['Tim Berners-Lee', 'MIT', '1955-06-08', 70, 'Male', 4], + ['Claude Shannon', 'MIT', '1916-04-30', 80, 'Male', null], + ['Barbara Liskov', 'MIT', '1939-11-07', 85, 'Female', null], + ['John McCarthy', 'Stanford University', '1927-09-04', 75, 'Male', 3], + ['Marvin Minsky', 'MIT', '1927-08-09', 76, 'Male', 9], + ['Larry Page', 'Stanford University', '1973-03-26', 64, 'Male', 12], + ['Sergey Brin', 'Stanford University', '1973-08-21', 63, 'Male', 11], + ['Don Norman', 'UC San Diego', '1935-12-25', 55, 'Male', null], + ['Brenda Laurel', 'UC Santa Cruz', '1950-11-20', 40, 'Female', 13] +]; + + +const papers = [ + ['On Computable Numbers, with an Application to the Entscheidungsproblem', 'Proceedings of the London Mathematical Society', '1936-11-12'], + ['The Analytical Engine', 'University of London', '1842-01-01'], + ['Theory of Games and Economic Behavior', 'Princeton University Press', '1944-03-01'], + ['The First Compiler', 'Harvard University', '1952-01-01'], + ['The Art of Computer Programming', 'Addison-Wesley', '1968-01-01'], + ['A Note on Two Problems in Connexion with Graphs', 'Numerische Mathematik', '1959-01-01'], + ['Information Management: A Proposal', 'CERN', '1989-03-01'], + ['A Mathematical Theory of Communication', 'Bell System Technical Journal', '1948-07-01'], + ['A Design Methodology for Reliable Software Systems', 'MIT', '1972-01-01'], + ['LISP: A Programming Language for Artificial Intelligence', 'Communications of the ACM', '1960-01-01'], + ['Steps Toward Artificial Intelligence', 'Proceedings of the IRE', '1961-01-01'], + ['The PageRank Citation Ranking: Bringing Order to the Web', 'Stanford University', '1998-01-01'], + ['The Anatomy of a Large-Scale Hypertextual Web Search Engine', 'Stanford University', '1999-01-01'], + ['The Design of Everyday Things', 'Basic Books', '1988-01-01'], + ['Computers as Theatre', 'Addison-Wesley', '1991-01-01'], + ['Quantum Mechanics and Path Integrals', 'Dover Publications', '1965-01-01'], + ['Reflections on Trusting Trust', 'Communications of the ACM', '1984-01-01'], + ['The Complexity of Theorem-Proving Procedures', 'ACM Symposium on Theory of Computing', '1971-01-01'], + ['Sketchpad: A Man-Machine Graphical Communication System', 'Spring Joint Computer Conference', '1963-01-01'], + ['No Silver Bullet: Essence and Accidents of Software Engineering', 'IFIP Congress', '1986-01-01'], + ['Introduction to Automata Theory, Languages, and Computation', 'Addison-Wesley', '1979-01-01'], + ['The Conceptual Framework of Computing', 'MIT', '1973-01-01'], + ['Design Patterns: Elements of Reusable Object-Oriented Software', 'Addison-Wesley', '1994-01-01'], + ['Computing Machinery and Intelligence', 'Mind', '1950-01-01'], + ['The CRISP-DM Process Model', 'IBM Research', '1999-01-01'], + ['As We May Think', 'The Atlantic', '1945-01-01'], + ['The Feynman Lectures on Physics', 'Addison-Wesley', '1964-01-01'], + ['The Mathematical Theory of Computation', 'Prentice Hall', '1972-01-01'], + ['The Structure of Scientific Revolutions', 'University of Chicago Press', '1962-01-01'], + ['Patterns of Software: Tales from the Software Community', 'Oxford University Press', '1996-01-01'] +]; + +const authorPapers = [ + [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], + [6, 6], [7, 7], [8, 8], [9, 9], [10, 10], + [11, 11], [12, 12], [13, 13], [14, 14], [15, 15], + [1, 16], [2, 17], [3, 18], [4, 19], [5, 20], + [6, 21], [7, 22], [8, 23], [9, 24], [10, 25], + [11, 26], [12, 27], [13, 28], [14, 29], [15, 30] +]; + +const insertAuthors = async (connection) => { + const insertAuthorQuery = ` + INSERT INTO authors (author_name, university, date_of_birth, h_index, gender, mentor) + VALUES ? + `; + await new Promise((resolve, reject) => { + connection.query(insertAuthorQuery, [authors], (err, results) => { + if (err) { + console.error('Error inserting authors:', err.stack); + reject(err); + return; + } + console.log('Authors inserted.'); + resolve(); + }); + }); +}; + +const insertResearchPapers = async (connection) => { + const insertPaperQuery = ` + INSERT INTO research_papers (paper_title, conference, publish_date) + VALUES ? + `; + await new Promise((resolve, reject) => { + connection.query(insertPaperQuery, [papers], (err, results) => { + if (err) { + console.error('Error inserting research papers:', err.stack); + reject(err); + return; + } + console.log('Research papers inserted.'); + resolve(); + }); + }); +}; + +// Insert data into author_papers junction table +const linkAuthorsToPapers = async (connection) => { + const insertAuthorPapersQuery = ` + INSERT INTO author_papers (author_id, paper_id) + VALUES ? + `; + await new Promise((resolve, reject) => { + connection.query(insertAuthorPapersQuery, [authorPapers], (err, results) => { + if (err) { + console.error('Error inserting author_papers:', err.stack); + reject(err); + return; + } + console.log('Author papers relationships inserted.'); + resolve(); + }); + }); +}; + + + +const exerciseTwo = async () => { + + const connection = createNewConnection(); + + connection.connect(err => { + if (err) { + return console.error('Connection error: ' + err.stack); + } + console.log('exercise_2: Connected!'); + }); + + try { + await useDatabase(connection) ; // Ensure the database is selected + await createResearchPapersTable(connection) ; // Create the research_papers table + await createAuthorPapersTable(connection) ; // Create the author_papers table + await insertAuthors(connection) ; // Insert authors into the authors table + await insertResearchPapers(connection) ; // Insert research papers + await linkAuthorsToPapers(connection) ; // Insert author-paper relationships + + console.log('Exercise steps completed.'); + } catch (err) { + console.error('Failed to set up the database:', err); + } finally { + connection.end(); + }; + }; + + + +exerciseTwo(); \ No newline at end of file diff --git a/Week2/exercise_3.js b/Week2/exercise_3.js new file mode 100644 index 000000000..6d68000ae --- /dev/null +++ b/Week2/exercise_3.js @@ -0,0 +1,81 @@ +import {createNewConnection, useDatabase} from './connection_query.js'; + +const connection = createNewConnection(); + +const getAuthorsAndMentors = (connection) => { + return new Promise((resolve, reject) => { + const query = ` + SELECT + a1.author_name AS Author, + a2.author_name AS Mentor + FROM + authors a1 + LEFT JOIN + authors a2 ON a1.mentor = a2.author_id; + `; + connection.query(query, (err, results) => { + if (err) { + console.error('Error fetching authors and mentors:', err.stack); + reject(err); + return; + } + console.log('Authors and their mentors:'); + console.table(results); + resolve(); + }); + }); +}; + + +const getAuthorsAndPapers = (connection) => { + return new Promise((resolve, reject) => { + const query = ` + SELECT + authors.*, + research_papers.paper_title + FROM + authors + LEFT JOIN + author_papers ON authors.author_id = author_papers.author_id + LEFT JOIN + research_papers ON author_papers.paper_id = research_papers.paper_id; + `; + connection.query(query, (err, results) => { + if (err) { + console.error('Error fetching authors and their papers:', err.stack); + reject(err); + return; + } + console.log('Authors and their published papers:'); + console.table(results); + resolve(); + }); + }); +}; + + +const exerciseThree = async () => { + /*Prevents the connection from running twice*/ +// Check if connect() has already been called on this instance + if (!connection._connectCalled) { + connection.connect(err => { + if (err) { + return console.error('Connection error: ' + err.stack); + } + console.log('Connected!'); + }); + } + try { + await useDatabase(connection); // Ensure the database is selected + await getAuthorsAndMentors(connection); // Get authors and their mentors + await getAuthorsAndPapers(connection); // Get authors and their published papers + } catch (err) { + console.error('An error occurred:', err); + } finally { + connection.end(); // Close the connection after all operations + } +}; + + +exerciseThree(); + diff --git a/Week2/exercise_4.js b/Week2/exercise_4.js new file mode 100644 index 000000000..7799276e8 --- /dev/null +++ b/Week2/exercise_4.js @@ -0,0 +1,164 @@ +import {createNewConnection, useDatabase} from './connection_query.js'; + +const connection = createNewConnection(); + +const getPapersAndAuthorCount = () => { + return new Promise((resolve, reject) => { + const query = ` + SELECT + research_papers.paper_title, + COUNT(author_papers.author_id) AS author_count + FROM + research_papers + LEFT JOIN + author_papers ON research_papers.paper_id = author_papers.paper_id + GROUP BY + research_papers.paper_id; + `; + connection.query(query, (err, results) => { + if (err) { + console.error('Error fetching papers and author count:', err.stack); + reject(err); + return; + } + console.log('Research papers and number of authors:'); + console.table(results); + resolve(); + }); + }); +}; + + +const getSumOfPapersByFemaleAuthors = () => { + return new Promise((resolve, reject) => { + const query = ` + SELECT + SUM(IF(authors.gender = 'Female', 1, 0)) AS female_author_paper_count + FROM + author_papers + LEFT JOIN + authors ON author_papers.author_id = authors.author_id; + `; + connection.query(query, (err, results) => { + if (err) { + console.error('Error fetching sum of papers by female authors:', err.stack); + reject(err); + return; + } + console.log('Sum of research papers published by all female authors:'); + console.table(results); + resolve(); + }); + }); +}; + + + +const getAverageHIndexPerUniversity = () => { + return new Promise((resolve, reject) => { + const query = ` + SELECT + university, + AVG(h_index) AS average_h_index + FROM + authors + GROUP BY + university; + `; + connection.query(query, (err, results) => { + if (err) { + console.error('Error fetching average h-index per university:', err.stack); + reject(err); + return; + } + console.log('Average h-index of all authors per university:'); + console.table(results); + resolve(); + }); + }); +}; + + +const getSumOfPapersPerUniversity = () => { + return new Promise((resolve, reject) => { + const query = ` + SELECT + authors.university, + COUNT(author_papers.paper_id) AS total_papers + FROM + authors + LEFT JOIN + author_papers ON authors.author_id = author_papers.author_id + GROUP BY + authors.university; + `; + connection.query(query, (err, results) => { + if (err) { + console.error('Error fetching sum of papers per university:', err.stack); + reject(err); + return; + } + console.log('Sum of research papers of the authors per university:'); + console.table(results); + resolve(); + }); + }); +}; + + + +const getMinMaxHIndexPerUniversity = () => { + return new Promise((resolve, reject) => { + const query = ` + SELECT + university, + MIN(h_index) AS min_h_index, + MAX(h_index) AS max_h_index + FROM + authors + GROUP BY + university; + `; + connection.query(query, (err, results) => { + if (err) { + console.error('Error fetching min and max h-index per university:', err.stack); + reject(err); + return; + } + console.log('Minimum and maximum h-index of all authors per university:'); + console.table(results); + resolve(); + }); + }); +}; + + +const exerciseFour = async () => { + /*Prevents the connection from running twice*/ +// Check if connect() has already been called on this instance + if (!connection._connectCalled) { + connection.connect(err => { + if (err) { + return console.error('Connection error: ' + err.stack); + } + console.log('Connected!'); + }); + } + try { + await useDatabase(); // Ensure the database is selected + await getPapersAndAuthorCount(); // Get all research papers and number of authors + await getSumOfPapersByFemaleAuthors();// Get sum of research papers by female authors + await getAverageHIndexPerUniversity();// Get average h-index per university + await getSumOfPapersPerUniversity(); // Get sum of research papers per university + await getMinMaxHIndexPerUniversity(); // Get min and max h-index per university + } catch (err) { + console.error('An error occurred:', err); + } finally { + connection.end(); // Close the connection after all operations + } +}; + +// Only run this script if it's executed directly (not imported) +if (require.main === module) { + exerciseFour(); +} From 47fc3d9e8e91b49df4c70ebe8348414375c3b636 Mon Sep 17 00:00:00 2001 From: Hanna Date: Thu, 22 Aug 2024 15:33:51 +0200 Subject: [PATCH 3/5] exercise_4.js revised --- Week2/exercise_4.js | 8 +- Week2/package-lock.json | 94 ++++++ Week2/package.json | 6 + Week3/DinnerClubERD.drawio | 252 +++++++++++++++ Week3/assignment/.$DinnerClubERD.drawio.bkp | 132 ++++++++ Week3/assignment/.$DinnerClubERD.drawio.dtmp | 298 ++++++++++++++++++ Week3/assignment/DinnerClubERD.drawio | 298 ++++++++++++++++++ Week3/assignment/connection_query.js | 30 ++ Week3/assignment/exercise_1.md | 17 + Week3/assignment/exercise_3.js | 28 ++ Week3/assignment/transaction.js | 97 ++++++ .../assignment/transactions-create-tables.js | 50 +++ .../assignment/transactions-insert-values.js | 47 +++ Week3/homework/mongodb/index.js | 67 ++-- Week3/homework/mongodb/package-lock.json | 242 ++++++++++++++ Week3/homework/mongodb/package.json | 7 + Week3/homework/mongodb/seedDatabase.js | 22 +- Week3/img.png | Bin 0 -> 60674 bytes 18 files changed, 1663 insertions(+), 32 deletions(-) create mode 100644 Week2/package-lock.json create mode 100644 Week2/package.json create mode 100644 Week3/DinnerClubERD.drawio create mode 100644 Week3/assignment/.$DinnerClubERD.drawio.bkp create mode 100644 Week3/assignment/.$DinnerClubERD.drawio.dtmp create mode 100644 Week3/assignment/DinnerClubERD.drawio create mode 100644 Week3/assignment/connection_query.js create mode 100644 Week3/assignment/exercise_1.md create mode 100644 Week3/assignment/exercise_3.js create mode 100644 Week3/assignment/transaction.js create mode 100644 Week3/assignment/transactions-create-tables.js create mode 100644 Week3/assignment/transactions-insert-values.js create mode 100644 Week3/homework/mongodb/package-lock.json create mode 100644 Week3/homework/mongodb/package.json create mode 100644 Week3/img.png diff --git a/Week2/exercise_4.js b/Week2/exercise_4.js index 7799276e8..b02fcbbc0 100644 --- a/Week2/exercise_4.js +++ b/Week2/exercise_4.js @@ -158,7 +158,11 @@ const exerciseFour = async () => { } }; -// Only run this script if it's executed directly (not imported) -if (require.main === module) { +// Only run this script if it's executed directly (not imported)~ +// ~because running the script on import causes duplicated connections + +/* `file://${process.argv[1]}` is used to get the full path to the script that is being executed +* `file://${process.argv[1]}` is compared to import.meta.url to determine if the current module is the main module*/ +if (import.meta.url === `file://${process.argv[1]}`) { exerciseFour(); } diff --git a/Week2/package-lock.json b/Week2/package-lock.json new file mode 100644 index 000000000..0378743a1 --- /dev/null +++ b/Week2/package-lock.json @@ -0,0 +1,94 @@ +{ + "name": "Week2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "mysql": "^2.18.1" + } + }, + "node_modules/bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", + "engines": { + "node": "*" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "dependencies": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + } + } +} diff --git a/Week2/package.json b/Week2/package.json new file mode 100644 index 000000000..ede26e1f0 --- /dev/null +++ b/Week2/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "dependencies": { + "mysql": "^2.18.1" + } +} diff --git a/Week3/DinnerClubERD.drawio b/Week3/DinnerClubERD.drawio new file mode 100644 index 000000000..ac1654ed1 --- /dev/null +++ b/Week3/DinnerClubERD.drawio @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Week3/assignment/.$DinnerClubERD.drawio.bkp b/Week3/assignment/.$DinnerClubERD.drawio.bkp new file mode 100644 index 000000000..cca04a713 --- /dev/null +++ b/Week3/assignment/.$DinnerClubERD.drawio.bkp @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Week3/assignment/.$DinnerClubERD.drawio.dtmp b/Week3/assignment/.$DinnerClubERD.drawio.dtmp new file mode 100644 index 000000000..b34d99eee --- /dev/null +++ b/Week3/assignment/.$DinnerClubERD.drawio.dtmp @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Week3/assignment/DinnerClubERD.drawio b/Week3/assignment/DinnerClubERD.drawio new file mode 100644 index 000000000..b34d99eee --- /dev/null +++ b/Week3/assignment/DinnerClubERD.drawio @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Week3/assignment/connection_query.js b/Week3/assignment/connection_query.js new file mode 100644 index 000000000..45c6bb19b --- /dev/null +++ b/Week3/assignment/connection_query.js @@ -0,0 +1,30 @@ +//C:\Users\knowl\Documents\hyf\databases\Week3\assignment\connection_query.js +import mysql from 'mysql'; + + +export const createNewConnection = () => { + return mysql.createConnection({ + host: 'localhost', + user: 'hyfuser', + password: 'hyfpassword', + multipleStatements: true, + }); +}; + +export const useDatabase = (connection) => { + return new Promise((resolve, reject) => { + const createDatabaseAndUse = ` + CREATE DATABASE IF NOT EXISTS transactions; + USE transactions; + `; + connection.query(createDatabaseAndUse, (err, results) => { + if (err) { + console.error('Error creating or selecting database:', err.stack); + reject(err); + return; + } + console.log('Database selected successfully.'); + resolve(); + }); + }); +}; \ No newline at end of file diff --git a/Week3/assignment/exercise_1.md b/Week3/assignment/exercise_1.md new file mode 100644 index 000000000..1ae40da44 --- /dev/null +++ b/Week3/assignment/exercise_1.md @@ -0,0 +1,17 @@ +## 1. What columns violate 1NF? +- columns `food_code` and `food_description`. They contain multiple values, which violates atomic value rule of 1NF +## 2. What entities do you recognize that could be extracted? +- member +- dinner +- venue +- food +- dinner_food + +## 3. Name all the tables and columns that would make a 3NF compliant solution. +- member +- dinner +- venue +- food +- dinner_food +- member_dinner +![img.png](../img.png) \ No newline at end of file diff --git a/Week3/assignment/exercise_3.js b/Week3/assignment/exercise_3.js new file mode 100644 index 000000000..7398b007c --- /dev/null +++ b/Week3/assignment/exercise_3.js @@ -0,0 +1,28 @@ + +// given function: +// function getPopulation(Country, name, code, cb) { +// // assuming that connection to the database is established and stored as conn +// conn.query( +// `SELECT Population FROM ${Country} WHERE Name = '${name}' and code = '${code}'`, +// function (err, result) { +// if (err) cb(err); +// if (result.length == 0) cb(new Error("Not found")); +// cb(null, result[0].name); +// } +// ); +// } +// code that would take advantage of SQL-injection: +// getPopulation("Netherlands", "'; --", "' OR '1'='1") +//SQL query: +// SELECT Population FROM Country WHERE Name = ''; -- ' and code = ' OR '1'='1' + +//Rewritten function +function getPopulation(Country, name, code, cb) { + // assuming that connection to the database is established and stored as conn + const query = `SELECT Population FROM ${Country} WHERE Name = ? and code = ?`; + conn.query(query, [name, code], function (err, result) { + if (err) return cb(err); + if (result.length == 0) return cb(new Error("Not found")); + cb(null, result[0].Population); + }); +} diff --git a/Week3/assignment/transaction.js b/Week3/assignment/transaction.js new file mode 100644 index 000000000..795c116e8 --- /dev/null +++ b/Week3/assignment/transaction.js @@ -0,0 +1,97 @@ +import {createNewConnection, useDatabase} from "./connection_query.js"; + +const connection = createNewConnection(); + +const transferAmount = (fromAccount, toAccount, amount) => { + return new Promise((resolve, reject) => { + connection.beginTransaction(err => { + if (err) return reject(err); + + // Deduct amount from the source account + const deductAmountQuery = ` + UPDATE account + SET balance = balance - ${connection.escape(amount)} + WHERE account_number = ${connection.escape(fromAccount)} + `; + + connection.query(deductAmountQuery, (err, result) => { + if (err) { + return connection.rollback(() => { + reject(err); + }); + } + + // Add amount to the destination account + const addAmountQuery = ` + UPDATE account + SET balance = balance + ${connection.escape(amount)} + WHERE account_number = ${connection.escape(toAccount)} + `; + + connection.query(addAmountQuery, (err, result) => { + if (err) { + return connection.rollback(() => { + reject(err); + }); + } + + // Log the change in the account_changes table for the source account + const logChangeFromAccount = ` + INSERT INTO account_changes (account_number, amount, changed_date, remark) + VALUES (${connection.escape(fromAccount)}, -${connection.escape(amount)}, NOW(), 'Transfer to account ${connection.escape(toAccount)}') + `; + + connection.query(logChangeFromAccount, (err, result) => { + if (err) { + return connection.rollback(() => { + reject(err); + }); + } + + // Log the change in the account_changes table for the destination account + const logChangeToAccount = ` + INSERT INTO account_changes (account_number, amount, changed_date, remark) + VALUES (${connection.escape(toAccount)}, ${connection.escape(amount)}, NOW(), 'Transfer from account ${connection.escape(fromAccount)}') + `; + + connection.query(logChangeToAccount, (err, result) => { + if (err) { + return connection.rollback(() => { + reject(err); + }); + } + + // Commit the transaction if everything is successful + connection.commit(err => { + if (err) { + return connection.rollback(() => { + reject(err); + }); + } + console.log('Transaction completed successfully.'); + resolve(); + }); + }); + }); + }); + }); + }); + }); +}; + + +connection.connect((err) => { + if (err) throw err; + console.log('Connected to the database.'); + + useDatabase(connection) + .then(() => { + return transferAmount(101, 102, 1000); + }) + .catch((error) => { + console.error('Error during transaction:', error); + }) + .finally(() => { + connection.end(); + }); +}); \ No newline at end of file diff --git a/Week3/assignment/transactions-create-tables.js b/Week3/assignment/transactions-create-tables.js new file mode 100644 index 000000000..bf29653e4 --- /dev/null +++ b/Week3/assignment/transactions-create-tables.js @@ -0,0 +1,50 @@ +//C:\Users\knowl\Documents\hyf\databases\Week3\assignment\transactions-create-tables.js +import {createNewConnection,useDatabase} from "./connection_query.js"; + +// Schema for the tables +const accountTable = { + account_number: 'INT PRIMARY KEY', + balance: 'DECIMAL(10, 2) NOT NULL' +}; + +const accountChangesTable = { + change_number: 'INT AUTO_INCREMENT PRIMARY KEY', + account_number: 'INT NOT NULL', + amount: 'DECIMAL(10, 2) NOT NULL', + changed_date: 'DATETIME NOT NULL', + remark: 'VARCHAR(255)' +}; + + +// Function to create a table +const createTable = (tableName, schema, foreignKeys='') => { + const columns = Object.entries(schema).map(([key, type]) => `${key} ${type}`).join(', '); + const query = `CREATE TABLE IF NOT EXISTS ${tableName} (${columns}${foreignKeys ? `, ${foreignKeys}` : ''})`; + + connection.query(query, (error, results, fields) => { + if (error) throw error; + console.log(`Table ${tableName} created successfully!`); + }); +}; + +const connection = createNewConnection(); + +// Connect to the database and create the tables +connection.connect((err) => { + if (err) throw err; + console.log('Connected to the database.'); + + + useDatabase(connection) + .then(() => { + createTable('account', accountTable); + createTable('account_changes', accountChangesTable, 'FOREIGN KEY (account_number) REFERENCES account(account_number)'); + }) + .catch((error) => { + console.error('Error setting up the database:', error); + }) + .finally(() => { + connection.end(); + }) + +}); \ No newline at end of file diff --git a/Week3/assignment/transactions-insert-values.js b/Week3/assignment/transactions-insert-values.js new file mode 100644 index 000000000..31223f57b --- /dev/null +++ b/Week3/assignment/transactions-insert-values.js @@ -0,0 +1,47 @@ +import {createNewConnection, useDatabase} from "./connection_query.js"; + +const connection = createNewConnection(); + +const accountData = [ + { account_number: 101, balance: 1000.00 }, + { account_number: 102, balance: 1500.50 }, + { account_number: 103, balance: 500.75 }, +]; + +const accountChangesData = [ + { account_number: 101, amount: -100.00, changed_date: '2024-08-01 10:00:00', remark: 'ATM Withdrawal' }, + { account_number: 102, amount: 200.00, changed_date: '2024-08-01 12:00:00', remark: 'Direct Deposit' }, + { account_number: 103, amount: -50.25, changed_date: '2024-08-02 09:30:00', remark: 'Online Purchase' }, + { account_number: 101, amount: 300.00, changed_date: '2024-08-02 14:00:00', remark: 'Salary Deposit' }, +]; + +// Function to insert data into a table +const insertData = (tableName, data) => { + const keys = Object.keys(data[0]); + const columns = keys.join(', '); + const values = data.map(row => keys.map(key => connection.escape(row[key])).join(', ')).join('), ('); + const query = `INSERT INTO ${tableName} (${columns}) VALUES (${values})`; + + connection.query(query, (error, results, fields) => { + if (error) throw error; + console.log(`Data inserted into table ${tableName} successfully!`); + }); +}; + + +connection.connect((err) => { + if (err) throw err; + console.log('Connected to the database.'); + + useDatabase(connection) + .then(() => { + insertData('account', accountData); + insertData('account_changes', accountChangesData); + }) + .catch((error) => { + console.error('Error inserting data into the database:', error); + }) + .finally(() => { + connection.end(); + }); +}); \ No newline at end of file diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 41ee8b618..1a7be1117 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -1,8 +1,13 @@ +//C:\Users\knowl\Documents\hyf\databases\Week3\homework\mongodb\index.js const { MongoClient, ServerApiVersion } = require("mongodb"); - const { seedDatabase } = require("./seedDatabase.js"); +const dotenv = require('dotenv'); +dotenv.config(); + + async function createEpisodeExercise(client) { + /** * We forgot to add the last episode of season 9. It has this information: * @@ -13,8 +18,17 @@ async function createEpisodeExercise(client) { // Write code that will add this to the collection! + const bobRossCollection = client.db("databaseWeek3").collection("bob_ross_episodes"); + + const newEpisode = { + episode: "S09E13", + title: "MOUNTAIN HIDE-AWAY", + elements: ["CIRRUS", "CLOUDS", "CONIFER", "DECIDIOUS", "GRASS", "MOUNTAIN", "MOUNTAINS", "RIVER", "SNOWY_MOUNTAIN", "TREE", "TREES"], + }; + + const result = await bobRossCollection.insertOne(newEpisode); console.log( - `Created season 9 episode 13 and the document got the id ${"TODO: fill in variable here"}` + `Created season 9 episode 13 and the document got the id ${result.insertedId}` ); } @@ -23,29 +37,33 @@ async function findEpisodesExercises(client) { * Complete the following exercises. * The comments indicate what to do and what the result should be! */ - + const bobRossCollection = client.db("databaseWeek3").collection("bob_ross_episodes"); // Find the title of episode 2 in season 2 [Should be: WINTER SUN] - + const episode2S2 = await bobRossCollection.findOne({ episode: "S02E02" }); console.log( - `The title of episode 2 in season 2 is ${"TODO: fill in variable here"}` + `The title of episode 2 in season 2 is ${episode2S2.title}` ); // Find the season and episode number of the episode called "BLACK RIVER" [Should be: S02E06] - + const blackRiverEpisode = await bobRossCollection.findOne({ title: "BLACK RIVER" }); console.log( - `The season and episode number of the "BLACK RIVER" episode is ${"TODO: fill in variable here"}` + `The season and episode number of the "BLACK RIVER" episode is ${blackRiverEpisode.episode}` ); // Find all of the episode titles where Bob Ross painted a CLIFF [Should be: NIGHT LIGHT, EVENING SEASCAPE, SURF'S UP, CLIFFSIDE, BY THE SEA, DEEP WILDERNESS HOME, CRIMSON TIDE, GRACEFUL WATERFALL] - + const cliffEpisodes = await bobRossCollection.find({ elements: "CLIFF" }).toArray(); + const cliffTitles = cliffEpisodes.map(ep => ep.title).join(", "); console.log( - `The episodes that Bob Ross painted a CLIFF are ${"TODO: fill in variable here"}` + `The episodes that Bob Ross painted a CLIFF are ${cliffTitles}` ); // Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT] - + const cliffAndLighthouseEpisodes = await bobRossCollection.find({ + elements: { $all: ["CLIFF", "LIGHTHOUSE"] }, + }).toArray(); + const cliffAndLighthouseTitles = cliffAndLighthouseEpisodes.map(ep => ep.title).join(", "); console.log( - `The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${"TODO: fill in variable here"}` + `The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${cliffAndLighthouseTitles}` ); } @@ -56,19 +74,25 @@ async function updateEpisodeExercises(client) { * * Note: do NOT change the data.json file */ - + const bobRossCollection = client.db("databaseWeek3").collection("bob_ross_episodes"); // Episode 13 in season 30 should be called BLUE RIDGE FALLS, yet it is called BLUE RIDGE FALLERS now. Fix that - + const updateResult = await bobRossCollection.updateOne( + { episode: "S30E13" }, + { $set: { title: "BLUE RIDGE FALLS" } } + ); console.log( - `Ran a command to update episode 13 in season 30 and it updated ${"TODO: fill in variable here"} episodes` + `Ran a command to update episode 13 in season 30 and it updated ${updateResult.modifiedCount} episodes` ); // Unfortunately we made a mistake in the arrays and the element type called 'BUSHES' should actually be 'BUSH' as sometimes only one bush was painted. // Update all of the documents in the collection that have `BUSHES` in the elements array to now have `BUSH` // It should update 120 episodes! - + const updateBushesResult = await bobRossCollection.updateMany( + { elements: "BUSHES" }, + { $set: { "elements.$": "BUSH" } } + ); console.log( - `Ran a command to update all the BUSHES to BUSH and it updated ${"TODO: fill in variable here"} episodes` + `Ran a command to update all the BUSHES to BUSH and it updated ${updateBushesResult.modifiedCount} episodes` ); } @@ -77,9 +101,10 @@ async function deleteEpisodeExercise(client) { * It seems an errand episode has gotten into our data. * This is episode 14 in season 31. Please remove it and verify that it has been removed! */ - + const bobRossCollection = client.db("databaseWeek3").collection("bob_ross_episodes"); + const deleteResult = await bobRossCollection.deleteOne({ episode: "S31E14" }); console.log( - `Ran a command to delete episode and it deleted ${"TODO: fill in variable here"} episodes` + `Ran a command to delete episode and it deleted ${deleteResult.deletedCount} episodes` ); } @@ -89,11 +114,7 @@ async function main() { `You did not set up the environment variables correctly. Did you create a '.env' file and add a package to create it?` ); } - const client = new MongoClient(process.env.MONGODB_URL, { - useNewUrlParser: true, - useUnifiedTopology: true, - serverApi: ServerApiVersion.v1, - }); + const client = new MongoClient(process.env.MONGODB_URL); try { await client.connect(); diff --git a/Week3/homework/mongodb/package-lock.json b/Week3/homework/mongodb/package-lock.json new file mode 100644 index 000000000..fc7c30d42 --- /dev/null +++ b/Week3/homework/mongodb/package-lock.json @@ -0,0 +1,242 @@ +{ + "name": "mongodb", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "dotenv": "^16.4.5", + "mongodb": "^6.8.0", + "mysql": "^2.18.1" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", + "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", + "engines": { + "node": "*" + } + }, + "node_modules/bson": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", + "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, + "node_modules/mongodb": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.8.0.tgz", + "integrity": "sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "dependencies": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + } + } +} diff --git a/Week3/homework/mongodb/package.json b/Week3/homework/mongodb/package.json new file mode 100644 index 000000000..bd7156aea --- /dev/null +++ b/Week3/homework/mongodb/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "dotenv": "^16.4.5", + "mongodb": "^6.8.0", + "mysql": "^2.18.1" + } +} diff --git a/Week3/homework/mongodb/seedDatabase.js b/Week3/homework/mongodb/seedDatabase.js index 99be6b3d8..5c8336225 100644 --- a/Week3/homework/mongodb/seedDatabase.js +++ b/Week3/homework/mongodb/seedDatabase.js @@ -1,4 +1,8 @@ +//C:\Users\knowl\Documents\hyf\databases\Week3\homework\mongodb\seedDatabase.js const data = require("./data.json"); +const dotenv = require('dotenv'); +dotenv.config(); + /** * This function will drop and recreate the collection of sample data in our csv file. @@ -7,15 +11,21 @@ const data = require("./data.json"); * @param {MongoClient} client - The client that is connected to your database */ const seedDatabase = async (client) => { + const db = client.db("databaseWeek3"); // Define the db variable + const collectionName = "bob_ross_episodes"; + const hasCollection = await client .db("databaseWeek3") .listCollections({ name: "bob_ross_episodes" }) .hasNext(); - if (hasCollection) { - const bobRossCollection = await client - .db("databaseWeek3") - .collection("bob_ross_episodes"); + if (!hasCollection) { + // Create the collection if it doesn't exist + await db.createCollection(collectionName); + } + + const bobRossCollection = db.collection(collectionName); + // Remove all the documents await bobRossCollection.deleteMany({}); @@ -41,9 +51,7 @@ const seedDatabase = async (client) => { // Add our documents await bobRossCollection.insertMany(documents); - } else { - throw Error("The collection `bob_ross_episodes` does not exist!"); - } + }; module.exports = { diff --git a/Week3/img.png b/Week3/img.png new file mode 100644 index 0000000000000000000000000000000000000000..9acb4e51b561dcb68da6117626b55e5f10af7faf GIT binary patch literal 60674 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 DkX>AP literal 0 HcmV?d00001 From dbf3dae7c9309e213a12acf16200ee4860ac93d6 Mon Sep 17 00:00:00 2001 From: Hanna Date: Sun, 25 Aug 2024 16:57:48 +0200 Subject: [PATCH 4/5] updated exercise_4.js --- Week2/exercise_3.js | 1 + Week2/exercise_4.js | 48 +++++++++++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/Week2/exercise_3.js b/Week2/exercise_3.js index 6d68000ae..e465a7b35 100644 --- a/Week2/exercise_3.js +++ b/Week2/exercise_3.js @@ -1,3 +1,4 @@ +//C:\Users\knowl\Documents\hyf\databases\Week2\exercise_3.js import {createNewConnection, useDatabase} from './connection_query.js'; const connection = createNewConnection(); diff --git a/Week2/exercise_4.js b/Week2/exercise_4.js index b02fcbbc0..1e8976738 100644 --- a/Week2/exercise_4.js +++ b/Week2/exercise_4.js @@ -1,8 +1,11 @@ +//C:\Users\knowl\Documents\hyf\databases\Week2\exercise_4.js import {createNewConnection, useDatabase} from './connection_query.js'; +import path from 'path'; +console.log(`running the file exercise_4.js`); const connection = createNewConnection(); -const getPapersAndAuthorCount = () => { +const getPapersAndAuthorCount = (connection) => { return new Promise((resolve, reject) => { const query = ` SELECT @@ -29,7 +32,7 @@ const getPapersAndAuthorCount = () => { }; -const getSumOfPapersByFemaleAuthors = () => { +const getSumOfPapersByFemaleAuthors = (connection) => { return new Promise((resolve, reject) => { const query = ` SELECT @@ -54,7 +57,7 @@ const getSumOfPapersByFemaleAuthors = () => { -const getAverageHIndexPerUniversity = () => { +const getAverageHIndexPerUniversity = (connection) => { return new Promise((resolve, reject) => { const query = ` SELECT @@ -79,7 +82,7 @@ const getAverageHIndexPerUniversity = () => { }; -const getSumOfPapersPerUniversity = () => { +const getSumOfPapersPerUniversity = (connection) => { return new Promise((resolve, reject) => { const query = ` SELECT @@ -107,7 +110,7 @@ const getSumOfPapersPerUniversity = () => { -const getMinMaxHIndexPerUniversity = () => { +const getMinMaxHIndexPerUniversity = (connection) => { return new Promise((resolve, reject) => { const query = ` SELECT @@ -133,9 +136,10 @@ const getMinMaxHIndexPerUniversity = () => { }; -const exerciseFour = async () => { +const exerciseFour = async (connection) => { /*Prevents the connection from running twice*/ // Check if connect() has already been called on this instance + console.log('Attempting to connect to the database...'); if (!connection._connectCalled) { connection.connect(err => { if (err) { @@ -143,14 +147,16 @@ const exerciseFour = async () => { } console.log('Connected!'); }); + } else { + console.log('Connection already established.'); } try { - await useDatabase(); // Ensure the database is selected - await getPapersAndAuthorCount(); // Get all research papers and number of authors - await getSumOfPapersByFemaleAuthors();// Get sum of research papers by female authors - await getAverageHIndexPerUniversity();// Get average h-index per university - await getSumOfPapersPerUniversity(); // Get sum of research papers per university - await getMinMaxHIndexPerUniversity(); // Get min and max h-index per university + await useDatabase(connection); // Ensure the database is selected + await getPapersAndAuthorCount(connection) ; // Get all research papers and number of authors + await getSumOfPapersByFemaleAuthors(connection) ;// Get sum of research papers by female authors + await getAverageHIndexPerUniversity(connection) ;// Get average h-index per university + await getSumOfPapersPerUniversity(connection) ; // Get sum of research papers per university + await getMinMaxHIndexPerUniversity(connection) ; // Get min and max h-index per university } catch (err) { console.error('An error occurred:', err); } finally { @@ -158,11 +164,23 @@ const exerciseFour = async () => { } }; + +// console.log(`import.meta.url: ${import.meta.url}`); +// console.log(`file://${process.argv[1]}`); +// const filePath = `file://${path.resolve(process.argv[1]).replace(/\\/g, '/')}`; +// console.log(`Normalized file path: ${filePath}`); + // Only run this script if it's executed directly (not imported)~ // ~because running the script on import causes duplicated connections /* `file://${process.argv[1]}` is used to get the full path to the script that is being executed * `file://${process.argv[1]}` is compared to import.meta.url to determine if the current module is the main module*/ -if (import.meta.url === `file://${process.argv[1]}`) { - exerciseFour(); -} +// if (import.meta.url === filePath) { +// console.log('Executing exerciseFour function.'); +// exerciseFour(connection); +// } else { +// console.log('Condition not met, not executing exerciseFour.'); +// } + + +exerciseFour(connection); From 59b95e38960065547ca08b619271f302fe434064 Mon Sep 17 00:00:00 2001 From: Hanna Date: Mon, 26 Aug 2024 14:13:49 +0200 Subject: [PATCH 5/5] updated .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 42212417c..708374c22 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules/ +/*/.env */node_modules */node_modules/ **/*.DS_Store