diff --git a/Week3/assignment/3.1-normalization.md b/Week3/assignment/3.1-normalization.md new file mode 100644 index 000000000..0363f730c --- /dev/null +++ b/Week3/assignment/3.1-normalization.md @@ -0,0 +1,64 @@ +# 3.1 — SQL Normalization + +## 1. What violates 1NF? + +- `food_code = "C1, C2"` → multiple values in a single column. +- `food_description = "Curry, Cake"` → same issue. +- `dinner_date` → inconsistent formats (`2020-03-15`, `20-03-2020`, `Mar 25 '20`). It should always be stored as `DATE`. + +## 2. What entities can be extracted? + +- **Members** (club members) +- **Dinners** (events) +- **Venues** (locations) +- **Foods** (dishes) + +Relationships: + +- Members ↔ Dinners (many-to-many, attendance) +- Dinners ↔ Foods (many-to-many, menu) +- Dinners → Venues (many-to-one) + +## 3. 3NF-compliant design + +```sql +-- Members table +CREATE TABLE members ( + member_id INTEGER PRIMARY KEY, + member_name TEXT NOT NULL, + member_address TEXT +); + +-- Venues table +CREATE TABLE venues ( + venue_code TEXT PRIMARY KEY, + venue_description TEXT NOT NULL +); + +-- Dinners table +CREATE TABLE dinners ( + dinner_id TEXT PRIMARY KEY, -- e.g. D00001001 + dinner_date DATE NOT NULL, + venue_code TEXT NOT NULL REFERENCES venues(venue_code) +); + +-- Foods table +CREATE TABLE foods ( + food_code TEXT PRIMARY KEY, -- e.g. C1, S1, P1 + food_description TEXT NOT NULL +); + +-- Attendance table (members attending dinners, many-to-many) +CREATE TABLE dinner_attendance ( + dinner_id TEXT NOT NULL REFERENCES dinners(dinner_id) ON DELETE CASCADE, + member_id INTEGER NOT NULL REFERENCES members(member_id) ON DELETE CASCADE, + PRIMARY KEY (dinner_id, member_id) +); + +-- Dinner-foods table (foods served at each dinner, many-to-many) +CREATE TABLE dinner_foods ( + dinner_id TEXT NOT NULL REFERENCES dinners(dinner_id) ON DELETE CASCADE, + food_code TEXT NOT NULL REFERENCES foods(food_code) ON DELETE RESTRICT, + PRIMARY KEY (dinner_id, food_code) +); +``` diff --git a/Week3/assignment/3.2/transaction.js b/Week3/assignment/3.2/transaction.js new file mode 100644 index 000000000..adb2e2329 --- /dev/null +++ b/Week3/assignment/3.2/transaction.js @@ -0,0 +1,75 @@ +import dotenv from "dotenv"; +import pg from "pg"; +dotenv.config(); + +const { Client } = pg; + +const db = new Client({ + host: process.env.PGHOST, + port: process.env.PGPORT, + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + database: process.env.PGDATABASE || "research", +}); + +async function transfer(fromAcc, toAcc, amount) { + await db.query("BEGIN"); + try { + // 1) Lock both accounts + const fromRes = await db.query( + "SELECT balance FROM account WHERE account_number = $1 FOR UPDATE", + [fromAcc] + ); + const toRes = await db.query( + "SELECT balance FROM account WHERE account_number = $1 FOR UPDATE", + [toAcc] + ); + + if (fromRes.rowCount === 0 || toRes.rowCount === 0) { + throw new Error("Account not found"); + } + + const fromBal = Number(fromRes.rows[0].balance); + if (fromBal < amount) throw new Error("Insufficient funds"); + + // 2) Update balances + await db.query( + "UPDATE account SET balance = balance - $1 WHERE account_number = $2", + [amount, fromAcc] + ); + await db.query( + "UPDATE account SET balance = balance + $1 WHERE account_number = $2", + [amount, toAcc] + ); + + // 3) Log changes + await db.query( + "INSERT INTO account_changes(account_number, amount, remark) VALUES ($1, $2, $3)", + [fromAcc, -amount, `transfer to ${toAcc}`] + ); + await db.query( + "INSERT INTO account_changes(account_number, amount, remark) VALUES ($1, $2, $3)", + [toAcc, amount, `transfer from ${fromAcc}`] + ); + + await db.query("COMMIT"); + console.log(` Transferred ${amount} from ${fromAcc} to ${toAcc}`); + } catch (e) { + await db.query("ROLLBACK"); + throw e; + } +} + +async function run() { + await db.connect(); + try { + await transfer(101, 102, 1000.0); + } finally { + await db.end(); + } +} + +run().catch((e) => { + console.error("Mistake", e.message); + process.exit(1); +}); diff --git a/Week3/assignment/3.2/transactions-create-tables.js b/Week3/assignment/3.2/transactions-create-tables.js new file mode 100644 index 000000000..a2e2fb1fb --- /dev/null +++ b/Week3/assignment/3.2/transactions-create-tables.js @@ -0,0 +1,45 @@ +import dotenv from "dotenv"; +import pg from "pg"; +dotenv.config(); +const { Client } = pg; + +const db = new Client({ + host: process.env.PGHOST, + port: process.env.PGPORT, + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + database: process.env.PGDATABASE || "research", +}); + +async function run() { + await db.connect(); + try { + await db.query(`DROP TABLE IF EXISTS account_changes;`); + await db.query(`DROP TABLE IF EXISTS account;`); + + await db.query(` + CREATE TABLE account ( + account_number INTEGER PRIMARY KEY, + balance NUMERIC(12,2) NOT NULL CHECK (balance >= 0) + ); + `); + + await db.query(` + CREATE TABLE account_changes ( + change_number BIGSERIAL PRIMARY KEY, + account_number INTEGER NOT NULL REFERENCES account(account_number) ON DELETE CASCADE, + amount NUMERIC(12,2) NOT NULL, + changed_date TIMESTAMPTZ NOT NULL DEFAULT now(), + remark TEXT NOT NULL + ); + `); + + console.log("tables created"); + } finally { + await db.end(); + } +} +run().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/Week3/assignment/3.2/transactions-insert-values.js b/Week3/assignment/3.2/transactions-insert-values.js new file mode 100644 index 000000000..caca80714 --- /dev/null +++ b/Week3/assignment/3.2/transactions-insert-values.js @@ -0,0 +1,49 @@ +// Week3/assignment/transactions-insert-values.js +// Inserts test accounts and initial seed transactions. + +import dotenv from "dotenv"; +import pg from "pg"; +dotenv.config(); + +const { Client } = pg; + +const db = new Client({ + host: process.env.PGHOST, + port: process.env.PGPORT, + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + database: process.env.PGDATABASE || "research", +}); + +async function run() { + await db.connect(); + try { + // Reset data + await db.query(`TRUNCATE account_changes, account RESTART IDENTITY;`); + + // Insert accounts + await db.query(` + INSERT INTO account(account_number, balance) VALUES + (101, 5000.00), + (102, 2000.00), + (103, 150.00); + `); + + // Insert account_changes log + await db.query(` + INSERT INTO account_changes(account_number, amount, remark) VALUES + (101, 5000.00, 'initial funding'), + (102, 2000.00, 'initial funding'), + (103, 150.00, 'initial funding'); + `); + + console.log("✅ Accounts and seed transactions inserted"); + } finally { + await db.end(); + } +} + +run().catch((e) => { + console.error("Mistake", e.message); + process.exit(1); +}); diff --git a/Week3/assignment/3.3-sql-injection.js b/Week3/assignment/3.3-sql-injection.js new file mode 100644 index 000000000..e7cb17465 --- /dev/null +++ b/Week3/assignment/3.3-sql-injection.js @@ -0,0 +1,46 @@ +import dotenv from "dotenv"; +import pg from "pg"; +dotenv.config(); + +const { Client } = pg; + +const db = new Client({ +host: process.env.PGHOST, +port: process.env.PGPORT, +user: process.env.PGUSER, +password: process.env.PGPASSWORD, +database: "world" // ← world DB with country table +}); + +async function vulnerableGetPopulation(name, code) { +// ❌ Vulnerable: direct string interpolation +const sql = `SELECT population FROM country WHERE name = '${name}' AND code = '${code}'`; +const res = await db.query(sql); +return res.rows; +} + +async function safeGetPopulation(name, code) { +// Safe: parameterized query +const sql = `SELECT population FROM country WHERE name = $1 AND code = $2`; +const res = await db.query(sql, [name, code]); +return res.rows; +} + +async function demo() { +await db.connect(); + +// Example of SQL injection +const hacked = await vulnerableGetPopulation(`' OR '1'='1`, `' OR '1'='1`); +console.log(" Injection result (all rows):", hacked.length); + +// Safe version +const normal = await safeGetPopulation("Netherlands", "NLD"); +console.log(" Safe query result:", normal); + +await db.end(); +} + +demo().catch(e => { +console.error(e); +process.exit(1); +}); diff --git a/Week3/assignment/mongo/index.js b/Week3/assignment/mongo/index.js new file mode 100644 index 000000000..f57e28734 --- /dev/null +++ b/Week3/assignment/mongo/index.js @@ -0,0 +1,55 @@ +import dotenv from "dotenv"; +import { MongoClient } from "mongodb"; +dotenv.config(); + +const uri = process.env.MONGODB_URI; +const dbName = process.env.MONGODB_DB || "databaseWeek3"; +const collName = process.env.MONGODB_COLLECTION || "bob_ross_episodes"; + +async function run() { + // 1) Connect + const client = new MongoClient(uri); + await client.connect(); + + try { + const db = client.db(dbName); + const coll = db.collection(collName); + + // 2) C — Create (insertOne) + const demoDoc = { + title: "Demo Episode", + elements: ["mountain", "tree", "lake"], + season: 99, + episode: 1, + }; + const insertRes = await coll.insertOne(demoDoc); + console.log("Inserted ID:", insertRes.insertedId.toString()); + + // 3) R — Read (find) + const mountains = await coll + .find({ elements: "mountain" }) + .project({ title: 1, season: 1, episode: 1, _id: 0 }) + .limit(5) + .toArray(); + console.log("Sample with 'mountain':", mountains); + + // 4) U — Update (updateMany) + const upd = await coll.updateMany( + { season: { $gte: 20 } }, + { $set: { hasVintage: true } } + ); + console.log("Updated docs:", upd.modifiedCount); + + // 5) D — Delete (deleteOne) — remove demo + const del = await coll.deleteOne({ _id: insertRes.insertedId }); + console.log("Deleted demo:", del.deletedCount); + } finally { + // 6) Close + await client.close(); + } +} + +run().catch((e) => { + console.error("Mongo error:", e.message); + process.exit(1); +}); diff --git a/Week3/assignment/package-lock.json b/Week3/assignment/package-lock.json new file mode 100644 index 000000000..6cf24df69 --- /dev/null +++ b/Week3/assignment/package-lock.json @@ -0,0 +1,176 @@ +{ + "name": "assignment", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "assignment", + "version": "1.0.0", + "license": "ISC", + "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/Week3/assignment/package.json b/Week3/assignment/package.json new file mode 100644 index 000000000..811da5567 --- /dev/null +++ b/Week3/assignment/package.json @@ -0,0 +1,17 @@ +{ + "name": "assignment", + "version": "1.0.0", + "main": "3.3-sql-injection.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "dotenv": "^17.2.2", + "mongodb": "^6.19.0" + } +} diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 41ee8b618..d85b993a0 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -1,94 +1,108 @@ const { MongoClient, ServerApiVersion } = require("mongodb"); - 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 DB_NAME = "databaseWeek3"; +const COLL_NAME = "bob_ross_episodes"; +// CREATE +async function createEpisodeExercise(client) { + const col = client.db(DB_NAME).collection(COLL_NAME); + + const doc = { + episode: "S09E13", + title: "MOUNTAIN HIDE-AWAY", + elements: [ + "CIRRUS", + "CLOUDS", + "CONIFER", + "DECIDIOUS", + "GRASS", + "MOUNTAIN", + "MOUNTAINS", + "RIVER", + "SNOWY_MOUNTAIN", + "TREE", + "TREES", + ], + }; + + const { insertedId } = await col.insertOne(doc); 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 ${insertedId}` ); } +// READ async function findEpisodesExercises(client) { - /** - * Complete the following exercises. - * The comments indicate what to do and what the result should be! - */ - - // Find the title of episode 2 in season 2 [Should be: WINTER SUN] - - console.log( - `The title of episode 2 in season 2 is ${"TODO: fill in variable here"}` - ); - - // 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"}` - ); + const col = client.db(DB_NAME).collection(COLL_NAME); - // 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] + // S02E02 -> WINTER SUN + const ep2s2 = await col.findOne({ episode: "S02E02" }); + console.log(`The title of episode 2 in season 2 is ${ep2s2.title}`); + // BLACK RIVER -> S02E06 + const blackRiver = await col.findOne({ title: "BLACK RIVER" }); console.log( - `The episodes that Bob Ross painted a CLIFF are ${"TODO: fill in variable here"}` + `The season and episode number of the "BLACK RIVER" episode is ${blackRiver.episode}` ); - // Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT] - + // episodes with CLIFF + const cliffDocs = await col.find({ elements: "CLIFF" }).toArray(); + const cliffTitles = cliffDocs.map((d) => d.title).join(", "); + console.log(`The episodes that Bob Ross painted a CLIFF are ${cliffTitles}`); + + // episodes with CLIFF and LIGHTHOUSE + const cliffLighthouseDocs = await col + .find({ elements: { $all: ["CLIFF", "LIGHTHOUSE"] } }) + .toArray(); + const cliffLighthouseTitles = cliffLighthouseDocs + .map((d) => d.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 ${cliffLighthouseTitles}` ); } +// UPDATE 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 col = client.db(DB_NAME).collection(COLL_NAME); + // S30E13 -> BLUE RIDGE FALLS + const updTitle = await col.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 ${updTitle.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! - + // BUSHES -> BUSH + const updBushes = await col.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 ${updBushes.modifiedCount} episodes` ); } +// DELETE 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 col = client.db(DB_NAME).collection(COLL_NAME); + const del = await col.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 ${del.deletedCount} episodes` ); } async function main() { - if (process.env.MONGODB_URL == null) { + if (!process.env.MONGODB_URL) { throw Error( - `You did not set up the environment variables correctly. Did you create a '.env' file and add a package to create it?` + `You did not set up the environment variables correctly. Did you create a '.env' file and add a package to load it?` ); } + const client = new MongoClient(process.env.MONGODB_URL, { useNewUrlParser: true, useUnifiedTopology: true, @@ -98,40 +112,19 @@ async function main() { try { await client.connect(); - // Seed our database + // Seed the database await seedDatabase(client); - // CREATE + // CRUD await createEpisodeExercise(client); - - // READ await findEpisodesExercises(client); - - // UPDATE await updateEpisodeExercises(client); - - // DELETE await deleteEpisodeExercise(client); } catch (err) { console.error(err); } finally { - // Always close the connection at the end - client.close(); + await client.close(); } } main(); - -/** - * In the end the console should read something like this: - -Created season 9 episode 13 and the document got the id 625e9addd11e82a59aa9ff93 -The title of episode 2 in season 2 is WINTER SUN -The season and episode number of the "BLACK RIVER" episode is S02E06 -The episodes that Bob Ross painted a CLIFF are NIGHT LIGHT, EVENING SEASCAPE, SURF'S UP, CLIFFSIDE, BY THE SEA, DEEP WILDERNESS HOME, CRIMSON TIDE, GRACEFUL WATERFALL -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 - -*/ diff --git a/Week3/homework/mongodb/package-lock.json b/Week3/homework/mongodb/package-lock.json new file mode 100644 index 000000000..069e5ff44 --- /dev/null +++ b/Week3/homework/mongodb/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "mongodb", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}