From 5fd8a16143824d61cfc16e52318144d4f91f3343 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 11:13:16 +0200 Subject: [PATCH 01/17] Ex1 is done --- Week3/MAKEME.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Week3/MAKEME.md b/Week3/MAKEME.md index 6e5f12e77..6e0b0bf94 100644 --- a/Week3/MAKEME.md +++ b/Week3/MAKEME.md @@ -49,9 +49,22 @@ Please help the manger by using the knowledge of database normal forms. Save all answers in a text file / MD file. 1. What columns violate 1NF? + +food_code, food_description + 2. What entities do you recognize that could be extracted? + +member, dinner, venue, food + 3. Name all the tables and columns that would make a 3NF compliant solution. +- member: member_id, member_name, member_address +- dinner: dinner_id, dinner_date, venue_code +- dinner_member: dinner_id, member_id +- venue: venue_code, venue_description +- dinner_food: dinner_id, food_code +- food: food_code, food_description + ``` +-----------+---------------+----------------+-----------+-------------+------------+-------------------+-----------+------------------+ | member_id | member_name | member_address | dinner_id | dinner_date | venue_code | venue_description | food_code | food_description | From b1f3baead8428cb7071d91257b1b0c8f96a6c7fb Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 12:12:00 +0200 Subject: [PATCH 02/17] transactions-create-tables code --- Week3/Ex2transactions/data.js | 164 ++++++++++++++++++ .../transactions-create-tables.js | 44 +++++ 2 files changed, 208 insertions(+) create mode 100644 Week3/Ex2transactions/data.js create mode 100644 Week3/Ex2transactions/transactions-create-tables.js diff --git a/Week3/Ex2transactions/data.js b/Week3/Ex2transactions/data.js new file mode 100644 index 000000000..279e5452f --- /dev/null +++ b/Week3/Ex2transactions/data.js @@ -0,0 +1,164 @@ +export const recipes = [ + { + recipe_id: 1, + recipe_name: "No-Bake Cheesecake", + }, + { + recipe_id: 2, + recipe_name: "Roasted Brussels Sprouts", + }, + { + recipe_id: 3, + recipe_name: "Mac & Cheese", + }, + { + recipe_id: 4, + recipe_name: "Tamagoyaki Japanese Omelette", + }, +]; + +export const categories = [ + { category_id: 1, category_name: "Cake" }, + { category_id: 2, category_name: "No-Bake" }, + { category_id: 3, category_name: "Vegetarian" }, + { category_id: 4, category_name: "Vegan" }, + { category_id: 5, category_name: "Gluten-Free" }, + { category_id: 6, category_name: "Japanese" }, +]; + +export const recipeCategories = [ + // No-Bake Cheesecake categories (recipe_id: 1) + { recipe_id: 1, category_id: 1 }, // Cake + { recipe_id: 1, category_id: 2 }, // No-Bake + { recipe_id: 1, category_id: 3 }, // Vegetarian + + // Roasted Brussels Sprouts categories (recipe_id: 2) + { recipe_id: 2, category_id: 4 }, // Vegan + { recipe_id: 2, category_id: 5 }, // Gluten-Free + + // Mac & Cheese categories (recipe_id: 3) + { recipe_id: 3, category_id: 3 }, // Vegetarian + + // Tamagoyaki Japanese Omelette categories (recipe_id: 4) + { recipe_id: 4, category_id: 3 }, // Vegetarian + { recipe_id: 4, category_id: 6 }, // Japanese +]; + +export const steps = [ + // No-Bake Cheesecake steps + { step_id: 1, step_content: "Beat Cream Cheese" }, + { step_id: 2, step_content: "Add condensed Milk and blend" }, + { step_id: 3, step_content: "Add Lemon Juice and blend" }, + { step_id: 4, step_content: "Add the mix to the pie crust" }, + { step_id: 5, step_content: "Spread the Cherry Jam" }, + { step_id: 6, step_content: "Place in refrigerator for 3h" }, + + // Roasted Brussels Sprouts steps + { step_id: 7, step_content: "Preheat the oven" }, + { step_id: 8, step_content: "Mix the ingredients in a bowl" }, + { step_id: 9, step_content: "Spread the mix on baking sheet" }, + { step_id: 10, step_content: "Bake for 30'" }, + + // Mac & Cheese steps + { step_id: 11, step_content: "Cook Macaroni for 8'" }, + { step_id: 12, step_content: "Melt butter in a saucepan" }, + { step_id: 13, step_content: "Add flour, salt, pepper and mix" }, + { step_id: 14, step_content: "Add Milk and mix" }, + { step_id: 15, step_content: "Cook until mix is smooth" }, + { step_id: 16, step_content: "Add cheddar cheese" }, + { step_id: 17, step_content: "Add the macaroni" }, + + // Tamagoyaki Japanese Omelette steps + { step_id: 18, step_content: "Beat the eggs" }, + { step_id: 19, step_content: "Add soya sauce, sugar and salt" }, + { step_id: 20, step_content: "Add oil to a sauce pan" }, + { step_id: 21, step_content: "Bring to medium heat" }, + { step_id: 22, step_content: "Add some mix to the sauce pan" }, + { step_id: 23, step_content: "Let it cook for 1'" }, + { step_id: 24, step_content: "Add some mix to the sauce pan" }, + { step_id: 25, step_content: "Remove pan from fire" }, +]; + +export const recipeSteps = [ + // No-Bake Cheesecake steps (recipe_id: 1) + { step_number: 1, recipe_id: 1, step_id: 1 }, + { step_number: 2, recipe_id: 1, step_id: 2 }, + { step_number: 3, recipe_id: 1, step_id: 3 }, + { step_number: 4, recipe_id: 1, step_id: 4 }, + { step_number: 5, recipe_id: 1, step_id: 5 }, + { step_number: 6, recipe_id: 1, step_id: 6 }, + + // Roasted Brussels Sprouts steps (recipe_id: 2) + { step_number: 1, recipe_id: 2, step_id: 7 }, + { step_number: 2, recipe_id: 2, step_id: 8 }, + { step_number: 3, recipe_id: 2, step_id: 9 }, + { step_number: 4, recipe_id: 2, step_id: 10 }, + + // Mac & Cheese steps (recipe_id: 3) + { step_number: 1, recipe_id: 3, step_id: 11 }, + { step_number: 2, recipe_id: 3, step_id: 12 }, + { step_number: 3, recipe_id: 3, step_id: 13 }, + { step_number: 4, recipe_id: 3, step_id: 14 }, + { step_number: 5, recipe_id: 3, step_id: 15 }, + { step_number: 6, recipe_id: 3, step_id: 16 }, + { step_number: 7, recipe_id: 3, step_id: 17 }, + + // Tamagoyaki Japanese Omelette steps (recipe_id: 4) + { step_number: 1, recipe_id: 4, step_id: 18 }, + { step_number: 2, recipe_id: 4, step_id: 19 }, + { step_number: 3, recipe_id: 4, step_id: 20 }, + { step_number: 4, recipe_id: 4, step_id: 21 }, + { step_number: 5, recipe_id: 4, step_id: 22 }, + { step_number: 6, recipe_id: 4, step_id: 23 }, + { step_number: 7, recipe_id: 4, step_id: 20 }, + { step_number: 8, recipe_id: 4, step_id: 24 }, + { step_number: 9, recipe_id: 4, step_id: 23 }, + { step_number: 10, recipe_id: 4, step_id: 25 }, +]; + +export const ingredients = [ + { ingredient_id: 1, ingredient_name: "Butter" }, + { ingredient_id: 2, ingredient_name: "Brussels Sprouts" }, + { ingredient_id: 3, ingredient_name: "Cherry Jam" }, + { ingredient_id: 4, ingredient_name: "Shredded Cheddar cheese" }, + { ingredient_id: 5, ingredient_name: "Condensed milk" }, + { ingredient_id: 6, ingredient_name: "Cream Cheese" }, + { ingredient_id: 7, ingredient_name: "Eggs" }, + { ingredient_id: 8, ingredient_name: "Flour" }, + { ingredient_id: 9, ingredient_name: "Lemon Juice" }, + { ingredient_id: 10, ingredient_name: "Macaroni" }, + { ingredient_id: 11, ingredient_name: "Milk" }, + { ingredient_id: 12, ingredient_name: "Olive oil" }, + { ingredient_id: 13, ingredient_name: "Pepper" }, + { ingredient_id: 14, ingredient_name: "Pie Crust" }, + { ingredient_id: 15, ingredient_name: "Salt" }, + { ingredient_id: 16, ingredient_name: "Sesame seeds" }, + { ingredient_id: 17, ingredient_name: "Soy sauce" }, + { ingredient_id: 18, ingredient_name: "Sugar" }, +]; + +export const recipeStepIngredients = [ + // No-Bake Cheesecake steps (recipe_id: 1) + { step_id: 1, ingredient_id: 6 }, // Cream Cheese + { step_id: 2, ingredient_id: 5 }, // Condensed milk + { step_id: 3, ingredient_id: 9 }, // Lemon Juice + { step_id: 4, ingredient_id: 14 }, // Pie Crust + { step_id: 5, ingredient_id: 3 }, // Cherry Jam + + // Mac & Cheese steps (recipe_id: 3) + { step_id: 11, ingredient_id: 10 }, // Macaroni + { step_id: 12, ingredient_id: 1 }, // Butter + { step_id: 13, ingredient_id: 8 }, // Flour + { step_id: 13, ingredient_id: 15 }, // Salt + { step_id: 13, ingredient_id: 13 }, // Pepper + { step_id: 14, ingredient_id: 11 }, // Milk + { step_id: 16, ingredient_id: 4 }, // Shredded Cheddar cheese + { step_id: 17, ingredient_id: 10 }, // Macaroni + + // Tamagoyaki Japanese Omelette steps (recipe_id: 4) + { step_id: 18, ingredient_id: 7 }, // Eggs + { step_id: 19, ingredient_id: 17 }, // Soy sauce + { step_id: 19, ingredient_id: 18 }, // Sugar + { step_id: 19, ingredient_id: 15 }, // Salt + { step_id: 20, ingredient_id: 12 }, // Olive oil +]; diff --git a/Week3/Ex2transactions/transactions-create-tables.js b/Week3/Ex2transactions/transactions-create-tables.js new file mode 100644 index 000000000..af06f4e10 --- /dev/null +++ b/Week3/Ex2transactions/transactions-create-tables.js @@ -0,0 +1,44 @@ +import { Client } from "pg"; + +// Database connection configuration +const config = { + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "transactions_week3", + port: 5432, +}; + +const client = new Client(config); + +async function createTables(client) { + const CREATE_ACCOUNT_TABLE = ` + CREATE TABLE IF NOT EXISTS account ( + account_number SMALLINT PRIMARY KEY, + balance INTEGER + )`; + + const CREATE_ACCOUNT_CHANGES_TABLE = ` + CREATE TABLE IF NOT EXISTS account_changes ( + change_number SERIAL PRIMARY KEY, + account_number SMALLINT, + amount INTEGER, + changed_date DATE, + remark VARCHAR(100), + FOREIGN KEY (account_number) REFERENCES account(account_number) ON DELETE CASCADE + )`; + try { + await client.connect(); + console.log("Connected to PostgreSQL database!"); + + // Create tables + await client.query(CREATE_ACCOUNT_TABLE); + await client.query(CREATE_ACCOUNT_CHANGES_TABLE); + } catch (error) { + console.error("Error creating tables:", error); + } finally { + await client.end(); + } +} + +createTables(client); From 963f7b1b6b6b3ab2108004c30a0ada006ac48037 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 12:17:39 +0200 Subject: [PATCH 03/17] fk name updated --- Week3/Ex2transactions/transactions-create-tables.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Week3/Ex2transactions/transactions-create-tables.js b/Week3/Ex2transactions/transactions-create-tables.js index af06f4e10..d49b4d05f 100644 --- a/Week3/Ex2transactions/transactions-create-tables.js +++ b/Week3/Ex2transactions/transactions-create-tables.js @@ -25,7 +25,7 @@ async function createTables(client) { amount INTEGER, changed_date DATE, remark VARCHAR(100), - FOREIGN KEY (account_number) REFERENCES account(account_number) ON DELETE CASCADE + CONSTRAINT fk_account_number FOREIGN KEY (account_number) REFERENCES account(account_number) ON DELETE CASCADE )`; try { await client.connect(); From 6ba134517483f49ab8f08c36ed9ed42dd6d0d291 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 12:58:00 +0200 Subject: [PATCH 04/17] transactions-insert-values done --- Week3/Ex2transactions/data.js | 186 +++--------------- .../transactions-insert-values.js | 46 +++++ 2 files changed, 77 insertions(+), 155 deletions(-) create mode 100644 Week3/Ex2transactions/transactions-insert-values.js diff --git a/Week3/Ex2transactions/data.js b/Week3/Ex2transactions/data.js index 279e5452f..18544b385 100644 --- a/Week3/Ex2transactions/data.js +++ b/Week3/Ex2transactions/data.js @@ -1,164 +1,40 @@ -export const recipes = [ +export const accounts = [ + { account_number: 101, balance: 5000 }, + { account_number: 102, balance: 12000 }, + { account_number: 103, balance: 7500 }, + { account_number: 104, balance: 3000 }, + { account_number: 105, balance: 9500 }, +]; + +export const account_changes = [ { - recipe_id: 1, - recipe_name: "No-Bake Cheesecake", + account_number: 101, + amount: 1000, + changed_date: "2025-09-01", + remark: "Salary deposit", }, { - recipe_id: 2, - recipe_name: "Roasted Brussels Sprouts", + account_number: 102, + amount: -500, + changed_date: "2025-09-02", + remark: "Grocery shopping", }, { - recipe_id: 3, - recipe_name: "Mac & Cheese", + account_number: 103, + amount: 2000, + changed_date: "2025-09-03", + remark: "Freelance payment", }, { - recipe_id: 4, - recipe_name: "Tamagoyaki Japanese Omelette", + account_number: 104, + amount: -1000, + changed_date: "2025-09-04", + remark: "Rent payment", + }, + { + account_number: 105, + amount: 1500, + changed_date: "2025-09-05", + remark: "Gift received", }, -]; - -export const categories = [ - { category_id: 1, category_name: "Cake" }, - { category_id: 2, category_name: "No-Bake" }, - { category_id: 3, category_name: "Vegetarian" }, - { category_id: 4, category_name: "Vegan" }, - { category_id: 5, category_name: "Gluten-Free" }, - { category_id: 6, category_name: "Japanese" }, -]; - -export const recipeCategories = [ - // No-Bake Cheesecake categories (recipe_id: 1) - { recipe_id: 1, category_id: 1 }, // Cake - { recipe_id: 1, category_id: 2 }, // No-Bake - { recipe_id: 1, category_id: 3 }, // Vegetarian - - // Roasted Brussels Sprouts categories (recipe_id: 2) - { recipe_id: 2, category_id: 4 }, // Vegan - { recipe_id: 2, category_id: 5 }, // Gluten-Free - - // Mac & Cheese categories (recipe_id: 3) - { recipe_id: 3, category_id: 3 }, // Vegetarian - - // Tamagoyaki Japanese Omelette categories (recipe_id: 4) - { recipe_id: 4, category_id: 3 }, // Vegetarian - { recipe_id: 4, category_id: 6 }, // Japanese -]; - -export const steps = [ - // No-Bake Cheesecake steps - { step_id: 1, step_content: "Beat Cream Cheese" }, - { step_id: 2, step_content: "Add condensed Milk and blend" }, - { step_id: 3, step_content: "Add Lemon Juice and blend" }, - { step_id: 4, step_content: "Add the mix to the pie crust" }, - { step_id: 5, step_content: "Spread the Cherry Jam" }, - { step_id: 6, step_content: "Place in refrigerator for 3h" }, - - // Roasted Brussels Sprouts steps - { step_id: 7, step_content: "Preheat the oven" }, - { step_id: 8, step_content: "Mix the ingredients in a bowl" }, - { step_id: 9, step_content: "Spread the mix on baking sheet" }, - { step_id: 10, step_content: "Bake for 30'" }, - - // Mac & Cheese steps - { step_id: 11, step_content: "Cook Macaroni for 8'" }, - { step_id: 12, step_content: "Melt butter in a saucepan" }, - { step_id: 13, step_content: "Add flour, salt, pepper and mix" }, - { step_id: 14, step_content: "Add Milk and mix" }, - { step_id: 15, step_content: "Cook until mix is smooth" }, - { step_id: 16, step_content: "Add cheddar cheese" }, - { step_id: 17, step_content: "Add the macaroni" }, - - // Tamagoyaki Japanese Omelette steps - { step_id: 18, step_content: "Beat the eggs" }, - { step_id: 19, step_content: "Add soya sauce, sugar and salt" }, - { step_id: 20, step_content: "Add oil to a sauce pan" }, - { step_id: 21, step_content: "Bring to medium heat" }, - { step_id: 22, step_content: "Add some mix to the sauce pan" }, - { step_id: 23, step_content: "Let it cook for 1'" }, - { step_id: 24, step_content: "Add some mix to the sauce pan" }, - { step_id: 25, step_content: "Remove pan from fire" }, -]; - -export const recipeSteps = [ - // No-Bake Cheesecake steps (recipe_id: 1) - { step_number: 1, recipe_id: 1, step_id: 1 }, - { step_number: 2, recipe_id: 1, step_id: 2 }, - { step_number: 3, recipe_id: 1, step_id: 3 }, - { step_number: 4, recipe_id: 1, step_id: 4 }, - { step_number: 5, recipe_id: 1, step_id: 5 }, - { step_number: 6, recipe_id: 1, step_id: 6 }, - - // Roasted Brussels Sprouts steps (recipe_id: 2) - { step_number: 1, recipe_id: 2, step_id: 7 }, - { step_number: 2, recipe_id: 2, step_id: 8 }, - { step_number: 3, recipe_id: 2, step_id: 9 }, - { step_number: 4, recipe_id: 2, step_id: 10 }, - - // Mac & Cheese steps (recipe_id: 3) - { step_number: 1, recipe_id: 3, step_id: 11 }, - { step_number: 2, recipe_id: 3, step_id: 12 }, - { step_number: 3, recipe_id: 3, step_id: 13 }, - { step_number: 4, recipe_id: 3, step_id: 14 }, - { step_number: 5, recipe_id: 3, step_id: 15 }, - { step_number: 6, recipe_id: 3, step_id: 16 }, - { step_number: 7, recipe_id: 3, step_id: 17 }, - - // Tamagoyaki Japanese Omelette steps (recipe_id: 4) - { step_number: 1, recipe_id: 4, step_id: 18 }, - { step_number: 2, recipe_id: 4, step_id: 19 }, - { step_number: 3, recipe_id: 4, step_id: 20 }, - { step_number: 4, recipe_id: 4, step_id: 21 }, - { step_number: 5, recipe_id: 4, step_id: 22 }, - { step_number: 6, recipe_id: 4, step_id: 23 }, - { step_number: 7, recipe_id: 4, step_id: 20 }, - { step_number: 8, recipe_id: 4, step_id: 24 }, - { step_number: 9, recipe_id: 4, step_id: 23 }, - { step_number: 10, recipe_id: 4, step_id: 25 }, -]; - -export const ingredients = [ - { ingredient_id: 1, ingredient_name: "Butter" }, - { ingredient_id: 2, ingredient_name: "Brussels Sprouts" }, - { ingredient_id: 3, ingredient_name: "Cherry Jam" }, - { ingredient_id: 4, ingredient_name: "Shredded Cheddar cheese" }, - { ingredient_id: 5, ingredient_name: "Condensed milk" }, - { ingredient_id: 6, ingredient_name: "Cream Cheese" }, - { ingredient_id: 7, ingredient_name: "Eggs" }, - { ingredient_id: 8, ingredient_name: "Flour" }, - { ingredient_id: 9, ingredient_name: "Lemon Juice" }, - { ingredient_id: 10, ingredient_name: "Macaroni" }, - { ingredient_id: 11, ingredient_name: "Milk" }, - { ingredient_id: 12, ingredient_name: "Olive oil" }, - { ingredient_id: 13, ingredient_name: "Pepper" }, - { ingredient_id: 14, ingredient_name: "Pie Crust" }, - { ingredient_id: 15, ingredient_name: "Salt" }, - { ingredient_id: 16, ingredient_name: "Sesame seeds" }, - { ingredient_id: 17, ingredient_name: "Soy sauce" }, - { ingredient_id: 18, ingredient_name: "Sugar" }, -]; - -export const recipeStepIngredients = [ - // No-Bake Cheesecake steps (recipe_id: 1) - { step_id: 1, ingredient_id: 6 }, // Cream Cheese - { step_id: 2, ingredient_id: 5 }, // Condensed milk - { step_id: 3, ingredient_id: 9 }, // Lemon Juice - { step_id: 4, ingredient_id: 14 }, // Pie Crust - { step_id: 5, ingredient_id: 3 }, // Cherry Jam - - // Mac & Cheese steps (recipe_id: 3) - { step_id: 11, ingredient_id: 10 }, // Macaroni - { step_id: 12, ingredient_id: 1 }, // Butter - { step_id: 13, ingredient_id: 8 }, // Flour - { step_id: 13, ingredient_id: 15 }, // Salt - { step_id: 13, ingredient_id: 13 }, // Pepper - { step_id: 14, ingredient_id: 11 }, // Milk - { step_id: 16, ingredient_id: 4 }, // Shredded Cheddar cheese - { step_id: 17, ingredient_id: 10 }, // Macaroni - - // Tamagoyaki Japanese Omelette steps (recipe_id: 4) - { step_id: 18, ingredient_id: 7 }, // Eggs - { step_id: 19, ingredient_id: 17 }, // Soy sauce - { step_id: 19, ingredient_id: 18 }, // Sugar - { step_id: 19, ingredient_id: 15 }, // Salt - { step_id: 20, ingredient_id: 12 }, // Olive oil ]; diff --git a/Week3/Ex2transactions/transactions-insert-values.js b/Week3/Ex2transactions/transactions-insert-values.js new file mode 100644 index 000000000..a54ed8e89 --- /dev/null +++ b/Week3/Ex2transactions/transactions-insert-values.js @@ -0,0 +1,46 @@ +import { accounts, account_changes } from "./data.js"; +import { Client } from "pg"; + +// Database connection configuration +const config = { + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "transactions_week3", + port: 5432, +}; +const client = new Client(config); + +async function seedDatabase(client) { + try { + await client.connect(); + console.log("Connected to PostgreSQL database!"); + + // Insert accounts + for (const account of accounts) { + const insertAccountQuery = { + text: "INSERT INTO account(account_number, balance) VALUES($1, $2) ON CONFLICT (account_number) DO NOTHING", + values: Object.values(account), + }; + await client.query(insertAccountQuery); + } + + // Insert account_changes + for (const change of account_changes) { + const insertChangeQuery = { + text: `INSERT INTO account_changes(account_number, amount, changed_date, remark) + VALUES($1, $2, $3, $4)`, + values: Object.values(change), + }; + await client.query(insertChangeQuery); + } + + // ...existing code... + } catch (error) { + console.error("Error seeding database:", error); + } finally { + await client.end(); + } +} + +seedDatabase(client); From 3b2b8ada6c0de7c74210a6243d500151ccb1644a Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 14:00:54 +0200 Subject: [PATCH 05/17] transaction.js completed --- Week3/Ex2transactions/transaction.js | 49 +++++++++++++++++++ .../transactions-insert-values.js | 21 +++----- 2 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 Week3/Ex2transactions/transaction.js diff --git a/Week3/Ex2transactions/transaction.js b/Week3/Ex2transactions/transaction.js new file mode 100644 index 000000000..c85f6f396 --- /dev/null +++ b/Week3/Ex2transactions/transaction.js @@ -0,0 +1,49 @@ +import { Client } from "pg"; +const config = { + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "transactions_week3", + port: 5432, +}; +const client = new Client(config); + +async function seedDatabase(client) { + try { + await client.connect(); + console.log("Connected to PostgreSQL database!"); + + const donator_account_number = 101; + const receiver_account_number = 102; + const amount = 1000; + const changed_date = new Date().toISOString().slice(0, 10); // 'YYYY-MM-DD' + const remark = `Transfer from 101 to 102`; + + await client.query("BEGIN"); + await client.query( + "UPDATE ACCOUNT SET balance = balance - $1 WHERE account_number = $2", + [amount, donator_account_number] + ); + await client.query( + "UPDATE ACCOUNT SET balance = balance + $1 WHERE account_number = $2", + [amount, receiver_account_number] + ); + await client.query( + "INSERT INTO account_changes(account_number, amount, changed_date, remark) VALUES($1, $2, $3, $4)", + [donator_account_number, -amount, changed_date, remark] + ); + await client.query( + "INSERT INTO account_changes(account_number, amount, changed_date, remark) VALUES($1, $2, $3, $4)", + [receiver_account_number, amount, changed_date, remark] + ); + await client.query("COMMIT"); + console.log("Transaction completed!"); + } catch (error) { + await client.query("ROLLBACK"); + console.error("Error seeding database:", error); + } finally { + await client.end(); + } +} + +seedDatabase(client); diff --git a/Week3/Ex2transactions/transactions-insert-values.js b/Week3/Ex2transactions/transactions-insert-values.js index a54ed8e89..fab7e3e85 100644 --- a/Week3/Ex2transactions/transactions-insert-values.js +++ b/Week3/Ex2transactions/transactions-insert-values.js @@ -1,7 +1,5 @@ import { accounts, account_changes } from "./data.js"; import { Client } from "pg"; - -// Database connection configuration const config = { host: "localhost", user: "hyfuser", @@ -11,31 +9,26 @@ const config = { }; const client = new Client(config); -async function seedDatabase(client) { +async function seedDatabase(client, accounts, account_changes) { try { await client.connect(); console.log("Connected to PostgreSQL database!"); - // Insert accounts for (const account of accounts) { - const insertAccountQuery = { + const INSERT_ACCOUNT_QUERY = { text: "INSERT INTO account(account_number, balance) VALUES($1, $2) ON CONFLICT (account_number) DO NOTHING", values: Object.values(account), }; - await client.query(insertAccountQuery); + await client.query(INSERT_ACCOUNT_QUERY); } - // Insert account_changes for (const change of account_changes) { - const insertChangeQuery = { - text: `INSERT INTO account_changes(account_number, amount, changed_date, remark) - VALUES($1, $2, $3, $4)`, + const INSERT_CHANGE_QUERY = { + text: `INSERT INTO account_changes(account_number, amount, changed_date, remark) VALUES($1, $2, $3, $4)`, values: Object.values(change), }; - await client.query(insertChangeQuery); + await client.query(INSERT_CHANGE_QUERY); } - - // ...existing code... } catch (error) { console.error("Error seeding database:", error); } finally { @@ -43,4 +36,4 @@ async function seedDatabase(client) { } } -seedDatabase(client); +seedDatabase(client, accounts, account_changes); From ee728b14622829fea510857c41891668544f2b67 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 14:03:33 +0200 Subject: [PATCH 06/17] cleaned up --- Week3/Ex2transactions/transaction.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Week3/Ex2transactions/transaction.js b/Week3/Ex2transactions/transaction.js index c85f6f396..93f4e8f91 100644 --- a/Week3/Ex2transactions/transaction.js +++ b/Week3/Ex2transactions/transaction.js @@ -39,7 +39,6 @@ async function seedDatabase(client) { await client.query("COMMIT"); console.log("Transaction completed!"); } catch (error) { - await client.query("ROLLBACK"); console.error("Error seeding database:", error); } finally { await client.end(); From 32ede356c2a987e2278e93b68b8dca1bbfc246b1 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 15:13:54 +0200 Subject: [PATCH 07/17] getPopulation function works --- Week3/Ex3sqlinjection/Ex3sqlinjection.js | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Week3/Ex3sqlinjection/Ex3sqlinjection.js diff --git a/Week3/Ex3sqlinjection/Ex3sqlinjection.js b/Week3/Ex3sqlinjection/Ex3sqlinjection.js new file mode 100644 index 000000000..d3ad9edf8 --- /dev/null +++ b/Week3/Ex3sqlinjection/Ex3sqlinjection.js @@ -0,0 +1,32 @@ +import { Client } from "pg"; +const config = { + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "world", + port: 5432, +}; +const conn = new Client(config); +await conn.connect(); +console.log("Connected to PostgreSQL database!"); + +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.rows[0].population); + } + ); +} + +getPopulation("country", "Netherlands", "NLD", function (err, population) { + if (err) { + console.error("Error:", err); + } else { + console.log("Population:", population); + } + conn.end(); +}); From a0941400cb092f7817ae856e2e719d01083eb8c8 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 16:18:53 +0200 Subject: [PATCH 08/17] the hacker's attack is limited --- Week3/Ex3sqlinjection/Ex3sqlinjection.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Week3/Ex3sqlinjection/Ex3sqlinjection.js b/Week3/Ex3sqlinjection/Ex3sqlinjection.js index d3ad9edf8..add45d330 100644 --- a/Week3/Ex3sqlinjection/Ex3sqlinjection.js +++ b/Week3/Ex3sqlinjection/Ex3sqlinjection.js @@ -26,7 +26,22 @@ getPopulation("country", "Netherlands", "NLD", function (err, population) { if (err) { console.error("Error:", err); } else { - console.log("Population:", population); + console.log("Netherland's population (the regular query):", population); } - conn.end(); }); + +// The resulting query becomes: SELECT Population FROM country WHERE Name = = '' OR 1=1 --' and code = '' OR 1=1 --' +// Since 1=1 is always true, this condition matches all rows in the country table. However, because the code only retrieves the first row from the result set, it returns only the population of the first country in the table. +getPopulation( + "country", + "' OR 1=1 --", + "' OR 1=1 --", + function (err, population) { + if (err) { + console.error("Error:", err); + } else { + console.log("the hacker's query):", population); + } + conn.end(); + } +); From 167b33a14bf701016c75428c30e19faee2f6ca9f Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 17:09:20 +0200 Subject: [PATCH 09/17] SQL injection proof code is written --- Week3/Ex3sqlinjection/Ex3sqlinjection.js | 30 +++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Week3/Ex3sqlinjection/Ex3sqlinjection.js b/Week3/Ex3sqlinjection/Ex3sqlinjection.js index add45d330..a3f28fd85 100644 --- a/Week3/Ex3sqlinjection/Ex3sqlinjection.js +++ b/Week3/Ex3sqlinjection/Ex3sqlinjection.js @@ -33,6 +33,34 @@ getPopulation("country", "Netherlands", "NLD", function (err, population) { // The resulting query becomes: SELECT Population FROM country WHERE Name = = '' OR 1=1 --' and code = '' OR 1=1 --' // Since 1=1 is always true, this condition matches all rows in the country table. However, because the code only retrieves the first row from the result set, it returns only the population of the first country in the table. getPopulation( + "country", + "Netherlands", + "' OR 1=1 --", + function (err, population) { + if (err) { + console.error("Error:", err); + } else { + console.log("the hacker's query 1:", population); + } + } +); + +// The user's input is treated as data only and not executable code here. However, it is not possible to demonstrate the advantages of this approach, as the function getPopulation can only handle results that contain an array of objects with the population property. Although the SQL code is executed and yields the desired result, it is not possible to extract it without modifying the initial function, so it serves as an SQL injection prevention of some sort. +function getPopulationSQLinjectProof(Country, name, code, cb) { + const allowedTables = new Set(["country"]); + const table = String(Country).toLowerCase(); + if (!allowedTables.has(table)) return cb(new Error("Invalid table")); + + const sql = `SELECT population FROM ${table} WHERE Name = $1 AND Code = $2`; + + conn.query(sql, [name, code], function (err, result) { + if (err) return cb(err); + if (!result) return cb(new Error("Not found")); + cb(null, result.rows); + }); +} + +getPopulationSQLinjectProof( "country", "' OR 1=1 --", "' OR 1=1 --", @@ -40,7 +68,7 @@ getPopulation( if (err) { console.error("Error:", err); } else { - console.log("the hacker's query):", population); + console.log("the hacker's query 2:", population); } conn.end(); } From f618c75ae6bb04c63942b3bb5e24c312e6635358 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 20:16:35 +0200 Subject: [PATCH 10/17] DB is seeded with the default config --- Week3/homework/mongodb/index.js | 3 +- Week3/homework/mongodb/seedDatabase.js | 2 + package-lock.json | 172 +++++++++++++++++++++++++ package.json | 6 + 4 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 41ee8b618..d1a454453 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -1,5 +1,6 @@ const { MongoClient, ServerApiVersion } = require("mongodb"); - +const { config: configDotenv } = require("dotenv"); +configDotenv({ silent: true }); const { seedDatabase } = require("./seedDatabase.js"); async function createEpisodeExercise(client) { diff --git a/Week3/homework/mongodb/seedDatabase.js b/Week3/homework/mongodb/seedDatabase.js index 99be6b3d8..1f3db46f8 100644 --- a/Week3/homework/mongodb/seedDatabase.js +++ b/Week3/homework/mongodb/seedDatabase.js @@ -1,4 +1,6 @@ const data = require("./data.json"); +const { config: configDotenv } = require("dotenv"); +configDotenv({ silent: true }); /** * This function will drop and recreate the collection of sample data in our csv file. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..5759ad78c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,172 @@ +{ + "name": "databases-Cohort53", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "dotenv": "^17.2.2", + "mongodb": "^6.19.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "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/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==", + "license": "MIT" + }, + "node_modules/mongodb": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.19.0.tgz", + "integrity": "sha512-H3GtYujOJdeKIMLKBT9PwlDhGrQfplABNF1G904w6r5ZXKWyv77aB0X9B+rhmaAwjtllHzaEkvi9mkGVZxs2Bw==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.4", + "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 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.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.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..85fe40b7a --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "dotenv": "^17.2.2", + "mongodb": "^6.19.0" + } +} From 5ceea470de08b83f5c5635949fd8010e991fe4e5 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 20:49:25 +0200 Subject: [PATCH 11/17] MOUNTAIN HIDE-AWAY inserted --- Week3/homework/mongodb/index.js | 34 ++++++++++++++++++-------- Week3/homework/mongodb/seedDatabase.js | 2 -- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index d1a454453..9810d7e34 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -4,18 +4,32 @@ configDotenv({ silent: true }); const { seedDatabase } = require("./seedDatabase.js"); async function createEpisodeExercise(client) { - /** - * We forgot to add the last episode of season 9. It has this information: - * - * episode: S09E13 - * title: MOUNTAIN HIDE-AWAY - * elements: ["CIRRUS", "CLOUDS", "CONIFER", "DECIDIOUS", "GRASS", "MOUNTAIN", "MOUNTAINS", "RIVER", "SNOWY_MOUNTAIN", "TREE", "TREES"] - */ - - // Write code that will add this to the collection! + const bobRossCollection = await client + .db("databaseWeek3") + .collection("bob_ross_episodes"); + + const documents = { + episode: "S09E13", + title: "MOUNTAIN HIDE-AWAY", + elements: [ + "CIRRUS", + "CLOUDS", + "CONIFER", + "DECIDIOUS", + "GRASS", + "MOUNTAIN", + "MOUNTAINS", + "RIVER", + "SNOWY_MOUNTAIN", + "TREE", + "TREES", + ], + }; + + const id = await bobRossCollection.insertOne(documents); 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 ${id.insertedId}` ); } diff --git a/Week3/homework/mongodb/seedDatabase.js b/Week3/homework/mongodb/seedDatabase.js index 1f3db46f8..99be6b3d8 100644 --- a/Week3/homework/mongodb/seedDatabase.js +++ b/Week3/homework/mongodb/seedDatabase.js @@ -1,6 +1,4 @@ const data = require("./data.json"); -const { config: configDotenv } = require("dotenv"); -configDotenv({ silent: true }); /** * This function will drop and recreate the collection of sample data in our csv file. From 9c129d388746fd5e87bf0bf5a2876d692ffb33e5 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 21:35:58 +0200 Subject: [PATCH 12/17] CLIFF works --- Week3/homework/mongodb/index.js | 36 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 9810d7e34..6313c41f2 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -2,13 +2,15 @@ const { MongoClient, ServerApiVersion } = require("mongodb"); const { config: configDotenv } = require("dotenv"); configDotenv({ silent: true }); const { seedDatabase } = require("./seedDatabase.js"); +let req; +let res; async function createEpisodeExercise(client) { const bobRossCollection = await client .db("databaseWeek3") .collection("bob_ross_episodes"); - const documents = { + req = { episode: "S09E13", title: "MOUNTAIN HIDE-AWAY", elements: [ @@ -26,35 +28,39 @@ async function createEpisodeExercise(client) { ], }; - const id = await bobRossCollection.insertOne(documents); + res = await bobRossCollection.insertOne(req); console.log( - `Created season 9 episode 13 and the document got the id ${id.insertedId}` + `Created season 9 episode 13 and the document got the id ${res.insertedId}` ); } async function findEpisodesExercises(client) { - /** - * Complete the following exercises. - * The comments indicate what to do and what the result should be! - */ - + const bobRossCollection = await client + .db("databaseWeek3") + .collection("bob_ross_episodes"); + req = { episode: "S02E02" }; + res = await bobRossCollection.findOne(req); // Find the title of episode 2 in season 2 [Should be: WINTER SUN] + console.log(`The title of episode 2 in season 2 is ${res.title}`); - console.log( - `The title of episode 2 in season 2 is ${"TODO: fill in variable here"}` - ); - + req = { title: "BLACK RIVER" }; + res = await bobRossCollection.findOne(req); // Find the season and episode number of the episode called "BLACK RIVER" [Should be: S02E06] - 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 ${res.episode}` ); + // console.log(JSON.stringify(res, null, 2)); + + req = { elements: "CLIFF" }; + res = await bobRossCollection.find(req).toArray(); // 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] 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 ${res + .map((item) => item.title) + .join(", ")}` ); // Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT] From fce9c83c210afaa1e17ab8f6a310edc376879584 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 21:43:58 +0200 Subject: [PATCH 13/17] NIGHT LIGHT works --- Week3/homework/mongodb/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 6313c41f2..b50457d2b 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -51,22 +51,22 @@ async function findEpisodesExercises(client) { `The season and episode number of the "BLACK RIVER" episode is ${res.episode}` ); - // console.log(JSON.stringify(res, null, 2)); - req = { elements: "CLIFF" }; res = await bobRossCollection.find(req).toArray(); // 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] - console.log( `The episodes that Bob Ross painted a CLIFF are ${res .map((item) => item.title) .join(", ")}` ); + req = { elements: { $all: ["CLIFF", "LIGHTHOUSE"] } }; + res = await bobRossCollection.find(req).toArray(); // Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT] - 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 ${res + .map((item) => item.title) + .join(", ")}` ); } From fc458cb81517b96216140e79320deabc741107ae Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 22:04:18 +0200 Subject: [PATCH 14/17] BLUE RIDGE FALLS works --- Week3/homework/mongodb/index.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index b50457d2b..85710a58e 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -71,17 +71,19 @@ async function findEpisodesExercises(client) { } async function updateEpisodeExercises(client) { - /** - * There are some problems in the initial data that was filled in. - * Let's use update functions to update this information. - * - * Note: do NOT change the data.json file - */ - - // Episode 13 in season 30 should be called BLUE RIDGE FALLS, yet it is called BLUE RIDGE FALLERS now. Fix that + const bobRossCollection = await client + .db("databaseWeek3") + .collection("bob_ross_episodes"); + bobRossCollection.updateOne( + { episode: "S30E13" }, + { $set: { title: "BLUE RIDGE FALLS" } } + ); + req = { episode: "S30E13" }; + res = await bobRossCollection.findOne(req); + // Episode 13 in season 30 should be called 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 ${res.title} 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. From d82f9b1497a1ce8529fd4ea66c498f802a6b6379 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 22:16:38 +0200 Subject: [PATCH 15/17] BUSHES completed --- Week3/homework/mongodb/index.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 85710a58e..89525b99d 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -86,12 +86,24 @@ async function updateEpisodeExercises(client) { `Ran a command to update episode 13 in season 30 and it updated ${res.title} 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` + res = await bobRossCollection.updateMany({ elements: "BUSHES" }, [ + { + $set: { + elements: { + $map: { + input: "$elements", + as: "el", + in: { + $cond: [{ $eq: ["$$el", "BUSHES"] }, "BUSH", "$$el"], + }, + }, + }, + }, + }, + ]); // It should update 120 episodes! - 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 ${res.modifiedCount} episodes` ); } From 9673efb6af7c9a71dde27f7ac94a5f5e59e52e39 Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 22:27:32 +0200 Subject: [PATCH 16/17] S31E14 deletion completed --- Week3/homework/mongodb/index.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 89525b99d..019a77509 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -108,13 +108,14 @@ async function updateEpisodeExercises(client) { } 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 = await client + .db("databaseWeek3") + .collection("bob_ross_episodes"); + req = { episode: "S31E14" }; + res = await bobRossCollection.deleteMany(req); + // This is episode 14 in season 31. Please remove it and verify that it has been removed! 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 ${res.deletedCount} episodes` ); } From 093ef6cda3856fc0e5ec234745e10b7603d7860b Mon Sep 17 00:00:00 2001 From: Yaroslav <1.yaroslav.kazeev@gmail.com> Date: Sun, 7 Sep 2025 22:32:24 +0200 Subject: [PATCH 17/17] cleaned up --- Week3/homework/mongodb/index.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 019a77509..c4917df56 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -75,15 +75,13 @@ async function updateEpisodeExercises(client) { .db("databaseWeek3") .collection("bob_ross_episodes"); - bobRossCollection.updateOne( + res = await bobRossCollection.updateOne( { episode: "S30E13" }, { $set: { title: "BLUE RIDGE FALLS" } } ); - req = { episode: "S30E13" }; - res = await bobRossCollection.findOne(req); // Episode 13 in season 30 should be called BLUE RIDGE FALLS. console.log( - `Ran a command to update episode 13 in season 30 and it updated ${res.title} episodes` + `Ran a command to update episode 13 in season 30 and it updated ${res.modifiedCount} episodes` ); res = await bobRossCollection.updateMany({ elements: "BUSHES" }, [ @@ -169,5 +167,4 @@ The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are NIGHT LIGHT Ran a command to update episode 13 in season 30 and it updated 1 episodes Ran a command to update all the BUSHES to BUSH and it updated 120 episodes Ran a command to delete episode and it deleted 1 episodes - */