diff --git a/Week1/Hadidreem17/setup_meetup_db.js b/Week1/Hadidreem17/setup_meetup_db.js new file mode 100644 index 000000000..9d8f424e9 --- /dev/null +++ b/Week1/Hadidreem17/setup_meetup_db.js @@ -0,0 +1,94 @@ +const { Client } = require("pg"); + +async function main() { +const client = new Client({ + user: "hyfuser", + host: "localhost", + database: "postgres", + password: "hyfpassword", + port: 5432, +}); + + await client.connect(); + + console.log("Dropping database if exists..."); + await client.query("DROP DATABASE IF EXISTS meetup"); + + console.log("Creating database meetup..."); + await client.query("CREATE DATABASE meetup"); + + await client.end(); + + + const meetupClient = new Client({ + user: "hyfuser", + host: "localhost", + database: "meetup", + password: "hyfpassword", + port: 5432, +}); + + await meetupClient.connect(); + + console.log("Creating tables..."); + + await meetupClient.query(` + CREATE TABLE Invitee ( + invitee_no SERIAL PRIMARY KEY, + invitee_name VARCHAR(100), + invited_by VARCHAR(100) + ); + `); + + await meetupClient.query(` + CREATE TABLE Room ( + room_no SERIAL PRIMARY KEY, + room_name VARCHAR(100), + floor_number INT + ); + `); + + await meetupClient.query(` + CREATE TABLE Meeting ( + meeting_no SERIAL PRIMARY KEY, + meeting_title VARCHAR(200), + starting_time TIMESTAMP, + ending_time TIMESTAMP, + room_no INT REFERENCES Room(room_no) + ); + `); + + console.log("Inserting sample data..."); + + await meetupClient.query(` + INSERT INTO Invitee (invitee_name, invited_by) VALUES + ('Sara', 'Ali'), + ('John', 'Lina'), + ('Maya', 'Omar'), + ('Noor', 'Samir'), + ('Adam', 'Lara');ssss + `); + + await meetupClient.query(` + INSERT INTO Room (room_name, floor_number) VALUES + ('Blue Room', 1), + ('Red Room', 2), + ('Green Room', 1), + ('Orange Room', 3), + ('VIP Room', 5); + `); + + await meetupClient.query(` + INSERT INTO Meeting (meeting_title, starting_time, ending_time, room_no) VALUES + ('Tech Meetup', NOW(), NOW() + INTERVAL '2 hours', 1), + ('Startup Pitch', NOW(), NOW() + INTERVAL '1 hour', 2), + ('Workshop JS', NOW(), NOW() + INTERVAL '3 hours', 3), + ('Design Basics', NOW(), NOW() + INTERVAL '4 hours', 4), + ('AI Conference', NOW(), NOW() + INTERVAL '5 hours', 5); + `); + + console.log("All done"); + await meetupClient.end(); +} + +main(); diff --git a/Week1/Hadidreem17/world_queries.js b/Week1/Hadidreem17/world_queries.js new file mode 100644 index 000000000..6dc696214 --- /dev/null +++ b/Week1/Hadidreem17/world_queries.js @@ -0,0 +1,83 @@ +const { Client } = require("pg"); + +async function main() { + const client = new Client({ + user: "hyfuser", + host: "localhost", + database: "world", + password: "hyfpassword", + port: 5432, + }); + + try { + await client.connect(); + + console.log("\n1. Countries with population > 8 million:"); + let result = await client.query(` + SELECT name FROM country WHERE population > 8000000; + `); + console.log(result.rows); + + console.log("\n2. Countries that contain 'land' in their names:"); + result = await client.query(` + SELECT name FROM country WHERE name ILIKE '%land%'; + `); + console.log(result.rows); + + console.log("\n3. Cities with population between 500k and 1 million:"); + result = await client.query(` + SELECT name FROM city + WHERE population BETWEEN 500000 AND 1000000; + `); + console.log(result.rows); + + console.log("\n4. Countries in Europe:"); + result = await client.query(` + SELECT name FROM country WHERE continent = 'Europe'; + `); + console.log(result.rows); + + console.log("\n5. Countries ordered by surface area DESC:"); + result = await client.query(` + SELECT name, surfacearea FROM country ORDER BY surfacearea DESC; + `); + console.log(result.rows); + + console.log("\n6. All cities in the Netherlands:"); + result = await client.query(` + SELECT name FROM city WHERE countrycode = 'NLD'; + `); + console.log(result.rows); + + console.log("\n7. Population of Rotterdam:"); + result = await client.query(` + SELECT population FROM city WHERE name = 'Rotterdam'; + `); + console.log(result.rows); + + console.log("\n8. Top 10 countries by surface area:"); + result = await client.query(` + SELECT name, surfacearea FROM country ORDER BY surfacearea DESC LIMIT 10; + `); + console.log(result.rows); + + console.log("\n9. Top 10 most populated cities:"); + result = await client.query(` + SELECT name, population FROM city ORDER BY population DESC LIMIT 10; + `); + console.log(result.rows); + + console.log("\n10. Total population of the world:"); + result = await client.query(` + SELECT SUM(population) AS world_population FROM country; + `); + console.log(result.rows); + + } catch (err) { + console.error("Error while running queries:", err); + } finally { + await client.end(); + } +} + +main(); diff --git a/Week4/LESSON_PLAN.md b/Week4/LESSON_PLAN.md deleted file mode 100644 index bf3b12e81..000000000 --- a/Week4/LESSON_PLAN.md +++ /dev/null @@ -1,288 +0,0 @@ -# Lesson Plan Databases Week 4 - -The lesson plan is primarily written for mentors so that they can use examples and anecdotes from this document in conjunction with the README and explain the concepts better during the session. - -## Topics - -1. Embedded vs Normalised data modeling -2. Advanced MongoDB operations -3. Pagination -4. Indexes in MongoDB -5. Transactions in MongoDB -6. SQL vs NoSQL databases - -## 1. Embedded vs Normalised data modeling - -### Explanation - -1. Embedded means that information is stored in a sub-object in a collection -2. Normalised means that information is stored in different collection but are referencing each other - -### Example - -Let's have a look at a bug logging database. - -#### The embedded way - -```js -const bugs = [ - { - timestamp: new Date(), - page: "about", - stacktrace: "", // removed for clarity - reporter: { - email: "rob@thebugcreator.com", - name: "Rob", - }, - }, -]; -``` - -#### The normalised way - -```js -const accounts = [ - { - id: ObjectId("507f191e810c19729de86032"), - email: "rob@thebugcreator.com", - name: "Rob", - }, -]; - -const bugs = [ - { - timestamp: new Date(), - page: "about", - stacktrace: "", // removed for clarity - reporterId: ObjectId("507f191e810c19729de86032"), - }, -]; -``` - -### Exercise - -Discuss the differences and what the advantages/disadvantages are to each approach. For example: - -Embedded allows for faster querying. -Normalized allows for less data duplication. - -### Essence - -There are advantages to both approaches and in the wild you will have to decide which to use every time. - -## 2. Advanced MongoDB operations - -### Explanation - -1. The `sort` command allows you to sort data that you get back from your query. -2. The `limit` command allows you to limit how many items you get back from your query. -3. The `aggregate` command allows the combination and calculation of data in one or more collections. - -### Example - -Let's assume a log database with the following information: - -```js -const bugs = [ - { - timestamp: new Date('2000-06-07T11:24:00'), - page: "about", - stacktrace: "", // removed for clarity - reporterId: ObjectId("507f191e810c19729de86032"), - }, { - timestamp: new Date('2000-06-06T12:23:00'), - page: "about", - stacktrace: "", // removed for clarity - reporterId: ObjectId("507f191e810c19729de86032"), - }. { - timestamp: new Date('2000-06-08T12:33:00'), - page: "contact", - stacktrace: "", // removed for clarity - reporterId: ObjectId("e810507f191de86032c19729"), - }, { - timestamp: new Date('2000-06-06T12:34:00'), - page: "home", - stacktrace: "", // removed for clarity - reporterId: ObjectId("e810507f191de86032c19729"), - } -]; -``` - -#### Sort - -If we want to sort the find query on the timestamp to find the latest bugs we can run the following query: - -```js -client.db("logging").collection("bugs").find().sort({ timestamp: -1 }); -``` - -#### Limit - -The above query will give you back all of the bugs which may not be what you want. You may want to have a dashboard with -the last 10 bugs, to do this we can use the limit command as follows: - -```js -client - .db("logging") - .collection("bugs") - .find() - .sort({ timestamp: -1 }) - .limit(10); -``` - -This will give only the last 10 bugs rather than all of them. Note that you can put the `sort` and `limit` commands in -any order! - -#### Aggregate - -Let's say we want to have a count of how many bugs appear per page. To do that we can do the following: - -```js -client - .db("logging") - .collection("bugs") - .aggregate([ - { - $group: { - _id: "$page", - count: { $count: {} }, - }, - }, - ]); -``` - -This will give back an object with the page field in the `_id` field and the number of bugs that were logged on that -page is in the `count` field! - -### Exercise - -- Sort all bugs by pages and then by timestamp. -- Group all the bugs by reporter and find the last reported bug by them - -### Essence - -MongoDB does a lot for you, the syntax is a little different than you are probably used to, but the documentation is -very detailed so make use of it! - -## 3. Pagination - -Using the same bugs collection, let's look at offset and cursor-based pagination using that collection. - -### Explanation - -Pagination is the splitting of content into different pages, which is done if there are too many results to put into one -web page. Think for example of the search results in Google where the user can click through to find what they are -looking for. We use pagination to restrict the amount of data that we send to the user as sometimes sending everything -will make the request too big. The application will become sluggish as it waits for the request which would result in -unhappy users. - -### Example - -Given the bugs database in the previous section let's implement both types of pagination: - -#### Offset-based - -```js -client - .db("logging") - .collection("bugs") - .find() - .sort({ timestamp: -1 }) - .limit(10) - .skip(20); -``` - -This would skip 20 results and then show the next 10. So would be on page 3 if we show 10 results per page! - -#### Cursor-based - -```js -const latestBugs = await client - .db("logging") - .collection("bugs") - .find({ - timestamp: { $lt: next || new Date() }, - }) - .sort({ timestamp: -1 }) - .limit(10); - -const cursorToGiveToUser = latestBugs[latestBugs.length - 1].timestamp; -``` - -Two important things here: - -- You need to always have the data sorted if you do cursor-based sorting as you are including the point you are at in - the query. -- You have to provide the user of your endpoint the information they need to send for the next query - -In the above code we do it with the timestamp, in other implementations an ID can be given. - -### Exercise - -Discuss the advantages and disadvantages of both approaches. - -### Essence - -Pagination can be very useful when dealing with big datasets to limit the amount of data needing to be prepared. There -are multiple ways of implementing pagination and it is dependent on the situation on how you want to implement it. -Something with a relatively low amount of pages can easily be done using the `skip/limit` combination, but when it comes -to huge data sets this approach becomes slow. A cursor-based approach is more complex to implement, but provides a -better performance. As usual it is up to you to balance what is needed for the situation. - -## 4. Indexes - -## 5. Transactions - -### Explanation - -The idea behind an index and a transaction should already be clear as it has been handled in SQL. So purely syntax here, -but if trainees cannot explain why we do these things, then go through it with them again. - -### Example - -```js -client.db("logging").collection("bugs").createIndex({ timestamp: -1 }); -``` - -This creates an index for sorting descending on timestamp which we have been querying a lot. - -```js -async function transferCredits(fromAccountId, toAccountId, amount) { - const accountsCollection = client.db("billing").collection("accounts"); - const session = client.startSession(); - - try { - session.withTransaction(async () => { - // Remove from fromUser - await accountsCollection.updateOne( - { _id: fromAccountId }, - { $inc: { credits: amount * -1 } }, - { session } - ); - - // Add to toUser - await accountsCollection.updateOne( - { _id: toAccountId }, - { $inc: { credits: amount } }, - { session } - ); - }); - } catch (err) { - await session.abortTransaction(); - } finally { - await session.endSession(); - } -} -``` - -### Exercise - -Discuss when and why to do indexes and transactions. What kind of scenarios are there. - -### Essence - -Both indexes and transactions have a cost attached to them, but can improve your databases performance and security! - -## 6. SQL vs NoSQL - -The prep exercise handles this, have a look at it [here](./QA_PREP_EXERCISE.md) diff --git a/Week4/MAKEME.md b/Week4/MAKEME.md deleted file mode 100644 index 1f85c51a1..000000000 --- a/Week4/MAKEME.md +++ /dev/null @@ -1,223 +0,0 @@ -# Assignment Databases Week 4 - -## **Todo list** - -1. Practice the concepts -2. Prep exercises -3. MongoDB exercises -4. Code along - -## 1. Practice the concepts - -Let's practice some advanced MongoDB queries. Have a look at the following exercises. If you feel unsure about the -commands you used last week then feel free to also do 01 and 02 of this series. You will need to go back to 00 to import -the data into your database, the datasets can be -found [here](https://github.com/mattdavis0351/mongodb/tree/master/datasets): - -- [Advanced MongoDB exercises](https://github.com/mattdavis0351/mongodb-labs/blob/master/exercises/03_advanced-mongo-queries.md) - -## 2. **Prep exercises** - -> Prep exercises are exercises that you should work on _before_ the session on Sunday. These are a little more difficult -> or show an important concept and as such are a great exercise to talk about with your mentor. Have a solution ready by -> Sunday as you may be asked to show what you did. - -The [QA_PREP_EXERCISE.md](./QA_PREP_EXERCISE.md) file explains what needs to be done for the prep exercise this week. -There will also be some questions at the bottom to think about. Go through them _before_ the session on Sunday as it -will be covered then. - -## 3. MongoDB exercises - -Let's create a fresh database for this week's assignment. You can create a database called `databaseWeek4` that you can -use for the following exercises. - -### 3.1 Exercise 1 : Aggregation - -Let's practice some aggregation queries, for that we will have to use some data and -luckily [kaggle](https://www.kaggle.com/) is a great website that provides datasets to use. In the `ex1-aggregation` -folder you will find a csv file with data for you. - -1. Find a way to get the data in the csv file into your MongoDB database. The documents should look like: - -```json -{ - _id: ObjectId(625ff77ada84ee8b5dd06e82), - Country: "Afghanistan", - Year: 1950, - Age: "20-24", - M: 374109, - F: 318392 -} -``` - -2. Write a function that will return the array of the total population (M + F over all age groups) for a given `Country` - per year. The result should look something like this, these are the values for `Netherlands`: - -```json -[ - { - _id: 1950, - countPopulation: 10042051 - }, - { - _id: 1960, - countPopulation: 11448815 - }, - { - _id: 1970, - countPopulation: 13001941 - }, - { - _id: 1980, - countPopulation: 14148410 - }, - { - _id: 1990, - countPopulation: 14965442 - }, - { - _id: 2000, - countPopulation: 15926188 - }, - { - _id: 2010, - countPopulation: 16682925 - }, - { - _id: 2020, - countPopulation: 17134872 - }, - { - _id: 2022, - countPopulation: 17211448 - } -]; -``` - -3. Write a function that will return all the information of each continent for a given `Year` and `Age` field but add - a new field `TotalPopulation` that will be the addition of `M` and `F`. For example, if I would give `2020` for - the `Year` and `100+` for the `Age` it should return something like this: - -```json -[ - { - _id: new - ObjectId( - "62600561b0a05834e3382cf8" - ), - Country: "AFRICA", - Year: 2020, - Age: "100+", - M: 1327, - F: 2723, - TotalPopulation: 4050 - }, - { - _id: new - ObjectId( - "62600561b0a05834e3382da0" - ), - Country: "ASIA", - Year: 2020, - Age: "100+", - M: 57019, - F: 207883, - TotalPopulation: 264902 - }, - { - _id: new - ObjectId( - "62600561b0a05834e33832a1" - ), - Country: "EUROPE", - Year: 2020, - Age: "100+", - M: 22579, - F: 102056, - TotalPopulation: 124635 - }, - { - _id: new - ObjectId( - "62600561b0a05834e33835d4" - ), - Country: "LATIN AMERICA AND THE CARIBBEAN", - Year: 2020, - Age: "100+", - M: 19858, - F: 49218, - TotalPopulation: 69076 - }, - { - _id: new - ObjectId( - "62600561b0a05834e3383946" - ), - Country: "NORTHERN AMERICA", - Year: 2020, - Age: "100+", - M: 22267, - F: 83419, - TotalPopulation: 105686 - }, - { - _id: new - ObjectId( - "62600561b0a05834e3383985" - ), - Country: "OCEANIA", - Year: 2020, - Age: "100+", - M: 1094, - F: 3980, - TotalPopulation: 5074 - } -]; -``` - -### 3.2 Exercise 2 : Transactions - -Just like last week, let's solve the same transaction problem but then in MongoDB. You can use the same data as in -Week3. Note that you will need to include some libraries which means you will need to set that up as well (create -a `ex2-transactions` folder for this). You will also probably want to create an `index.js` that calls the functions we -will create to test it out, we leave the implementation of that up to you. Now let's get started, we will split our code -into multiple files again, first being the setup file: - -1. Create a `setup.js` file. -2. It should clean up the `accounts` array and then fill it with some sample data. Just like last last week we want an - account document to have an `account_number` and `balance` field. Then it should have another field - called `account_changes` that is an array that contains the fields: `change_number, amount, changed_date, remark`. -3. It's probably best to make this a function that you can export and call - -Then it is time to write our transaction function: - -1. Create a `transfer.js` file that will hold our `transfer` function. -2. It should transfer money from one account to another, so it will need to know the following things: from which - account, to which account, the amount and the remark for this transaction. -3. This should update the balances of both accounts and for each account add a change to the list. The change number - should be incremented, so if the latest `change_number` is 30, the `change_number` for the new change should be 31. -4. Test that it works by calling the function to transfer 1000 from account number 101 to account number 102. - -Submit the `setup.js` and `transfer.js` files. - -## 4. Code Along - -This week we have a small code along to show how to implement pagination and make it reusable for all your routes! - -- [Paginated API with Node and Mongoose](https://www.youtube.com/watch?v=ZX3qt0UWifc). We will be working in Mongoose in - the final project, but this gives a little teaser on what mongoose is! - -## SUBMIT YOUR ASSIGNMENT! - -After you've finished your todo list it's time to show us what you got! The assignment that needs to be submitted is the -following: - -1. MongoDB exercises - -Upload your code to your forked Databases repository in GitHub. Make a pull request to HackYourAssignments's forked -repository. - -> Forgotten how to upload your assignment? Go through the [guide](../hand-in-assignments-guide.md) to learn how to do this -> again. - -_Deadline Tuesday 23.59 CET_ diff --git a/Week4/QA_PREP_EXERCISE.md b/Week4/QA_PREP_EXERCISE.md deleted file mode 100644 index 67a36ba1b..000000000 --- a/Week4/QA_PREP_EXERCISE.md +++ /dev/null @@ -1,18 +0,0 @@ -# Prep exercise week 4 - -As a preparation step for the upcoming Q&A, you need to work on the following exercise, which is based on the prep -exercise of the previous week. - -## Exercise - -Last week you updated your database to be normalized. Now that you have some more NoSQL knowledge, convert your database -to a document-based database. Think about the following: - -- What are the collections? -- What information will you embed in a document and which will you store normalised? - -## Discussion (Try to write answers to these questions in text, provide queries and commands when necessary) - -- What made you decide when to embed information? What assumptions did you make? -- If you were given PostgreSQL and MongoDB as choices to build the recipe's database at the beginning, which one would you - choose and why? \ No newline at end of file diff --git a/Week4/README.md b/Week4/README.md deleted file mode 100644 index 946fe1922..000000000 --- a/Week4/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Reading Material Databases Week 4 - -## Agenda - -These are the topics for week 4, all in MongoDB: - -- [Embedded vs Normalised data modeling](https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/embedded-vs-normalised.md) -- [Advanced MongoDB operations](https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/advanced-operations.md) -- [Pagination](https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/pagination.md) -- [Indexes in MongoDB](https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/indexes.md) -- [Transactions in MongoDB](https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/transactions.md) -- [SQL vs NoSQL databases](https://hackyourfuture.github.io/study/#/databases/sql-vs-nosql-databases.md) - -## Week goals - -This week we are going to focus fully on MongoDB as the main example of a document-based database which is the most common subset of NoSQL databases. Let's start with some more advanced data modeling ideas by talking about the difference in [Embedded and Normalised data modeling (https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/embedded-vs-normalised.md). - -To work with more complex data models, we also need some more advanced MongoDB operations. Have a look at the [Advanced MongoDB operations](https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/advanced-operations.md) section to get acquainted with some of the more commonly used ones. After that, have a look at the [Pagination](https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/pagination.md) section for a common use case with these functions. - -Next let's address some of the topics that are theoretically the same as in SQL, but this time looking at how they work in MongoDB. Have a look at [Indexes](https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/indexes.md) and [Transactions (https://hackyourfuture.github.io/study/#/databases/nosql/mongodb/transactions.md). - -Last, now that you have an idea of what both SQL and NoSQL mean and how they work, you're have to ask yourself when to use one over the other in the [SQL vs NoSQL](https://hackyourfuture.github.io/study/#/databases/sql-vs-nosql-databases.md) section. - -## Finished? - -Have you gone through all of the materials? High five! If you feel ready to get practical, click [here](./MAKEME.md). diff --git a/Week4/homework/ex2-transactions/index.js b/Week4/homework/ex2-transactions/index.js new file mode 100644 index 000000000..71b8bf956 --- /dev/null +++ b/Week4/homework/ex2-transactions/index.js @@ -0,0 +1,20 @@ +const { setupAccounts } = require("./setup"); +const { transfer } = require("./transfer"); + +async function main() { + try { + console.log("Running setup..."); + await setupAccounts(); + + console.log("Setup complete!"); + + console.log("Running transfer..."); + await transfer(101, 102, 1000, "Test transfer from 101 to 102"); + + console.log("Transfer complete!"); + } catch (err) { + console.error("Error:", err); + } +} + +main(); diff --git a/Week4/homework/ex2-transactions/setup.js b/Week4/homework/ex2-transactions/setup.js new file mode 100644 index 000000000..d5f116a24 --- /dev/null +++ b/Week4/homework/ex2-transactions/setup.js @@ -0,0 +1,66 @@ +const { MongoClient } = require("mongodb"); + +const uri = "mongodb://127.0.0.1:27017"; + +async function setupAccounts() { + const client = new MongoClient(uri); + + try { + await client.connect(); + const db = client.db("databaseWeek4"); + const accounts = db.collection("accounts"); + + await accounts.deleteMany({}); + + const now = new Date(); + + const sampleAccounts = [ + { + account_number: 101, + balance: 5000, + account_changes: [ + { + change_number: 1, + amount: 5000, + changed_date: now, + remark: "Initial deposit", + }, + ], + }, + { + account_number: 102, + balance: 3000, + account_changes: [ + { + change_number: 1, + amount: 3000, + changed_date: now, + remark: "Initial deposit", + }, + ], + }, + { + account_number: 103, + balance: 2000, + account_changes: [ + { + change_number: 1, + amount: 2000, + changed_date: now, + remark: "Initial deposit", + }, + ], + }, + ]; + + await accounts.insertMany(sampleAccounts); + + console.log(" Accounts collection initialized"); + } catch (err) { + console.error(" Error in setupAccounts:", err); + } finally { + await client.close(); + } +} + +module.exports = { setupAccounts }; diff --git a/Week4/homework/ex2-transactions/transfer.js b/Week4/homework/ex2-transactions/transfer.js new file mode 100644 index 000000000..b3d8393b1 --- /dev/null +++ b/Week4/homework/ex2-transactions/transfer.js @@ -0,0 +1,79 @@ +const { MongoClient } = require("mongodb"); + +const uri = "mongodb://127.0.0.1:27017"; + +async function transfer(fromAccountNumber, toAccountNumber, amount, remark) { + const client = new MongoClient(uri); + + try { + await client.connect(); + const db = client.db("databaseWeek4"); + const accounts = db.collection("accounts"); + + const fromAccount = await accounts.findOne({ + account_number: fromAccountNumber, + }); + const toAccount = await accounts.findOne({ + account_number: toAccountNumber, + }); + + if (!fromAccount || !toAccount) { + throw new Error("One or both accounts not found"); + } + + if (fromAccount.balance < amount) { + throw new Error("Insufficient funds in source account"); + } + + const now = new Date(); + + const nextChangeFrom = + (fromAccount.account_changes?.[fromAccount.account_changes.length - 1] + ?.change_number || 0) + 1; + + const nextChangeTo = + (toAccount.account_changes?.[toAccount.account_changes.length - 1] + ?.change_number || 0) + 1; + + await accounts.updateOne( + { account_number: fromAccountNumber }, + { + $inc: { balance: -amount }, + $push: { + account_changes: { + change_number: nextChangeFrom, + amount: -amount, + changed_date: now, + remark, + }, + }, + } + ); + + await accounts.updateOne( + { account_number: toAccountNumber }, + { + $inc: { balance: amount }, + $push: { + account_changes: { + change_number: nextChangeTo, + amount: amount, + changed_date: now, + remark, + }, + }, + } + ); + + console.log( + `Transferred ${amount} from ${fromAccountNumber} to ${toAccountNumber}` + ); + } catch (err) { + console.error("Transfer failed:", err.message); + throw err; + } finally { + await client.close(); + } +} + +module.exports = { transfer }; diff --git a/Week4/homework/index.js b/Week4/homework/index.js new file mode 100644 index 000000000..4a9efaf74 --- /dev/null +++ b/Week4/homework/index.js @@ -0,0 +1,83 @@ +const { MongoClient } = require("mongodb"); +const uri = "mongodb://127.0.0.1:27017"; +const client = new MongoClient(uri); + +async function getPopulationByCountryPerYear(countryName) { + const db = client.db("databaseWeek4"); + const collection = db.collection("population"); + + const result = await collection + .aggregate([ + + { $match: { Country: countryName } }, + + { + $group: { + _id: "$Year", + countPopulation: { + $sum: { + $add: [ + { $toLong: "$M" }, + { $toLong: "$F" }, + ], + }, + }, + }, + }, + + { $sort: { _id: 1 } }, + ]) + .toArray(); + + return result; +} + +async function getContinentsForYearAndAge(year, age) { + const db = client.db("databaseWeek4"); + const collection = db.collection("population"); + + const result = await collection + .aggregate([ + { + $match: { + Year: year, + Age: age, + }, + }, + + { + $addFields: { + TotalPopulation: { + $add: [ + { $toLong: "$M" }, + { $toLong: "$F" }, + ], + }, + }, + }, + ]) + .toArray(); + + return result; +} + +async function main() { + try { + await client.connect(); + console.log("Connected to MongoDB"); + + const byYear = await getPopulationByCountryPerYear("Netherlands"); + console.log("Population of Netherlands per year:"); + console.log(byYear); + + const continents = await getContinentsForYearAndAge(2020, "100+"); + console.log(" Continents for 2020, Age 100+ :"); + console.log(continents); + } catch (err) { + console.error(err); + } finally { + await client.close(); + } +} + +main(); diff --git a/Week4/homework/package-lock.json b/Week4/homework/package-lock.json new file mode 100644 index 000000000..c2cc1db37 --- /dev/null +++ b/Week4/homework/package-lock.json @@ -0,0 +1,166 @@ +{ + "name": "database-assignment-week4", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "database-assignment-week4", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "mongodb": "^7.0.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.0.tgz", + "integrity": "sha512-ZHzx7Z3rdlWL1mECydvpryWN/ETXJiCxdgQKTAH+djzIPe77HdnSizKBDi1TVDXZjXyOj2IqEG/vPw71ULF06w==", + "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": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/bson": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.0.0.tgz", + "integrity": "sha512-Kwc6Wh4lQ5OmkqqKhYGKIuELXl+EPYSCObVE6bWsp1T/cGkOCBN0I8wF/T44BiuhHyNi1mmKVPXk60d41xZ7kw==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.19.0" + } + }, + "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": "7.0.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz", + "integrity": "sha512-vG/A5cQrvGGvZm2mTnCSz1LUcbOPl83hfB6bxULKQ8oFZauyox/2xbZOoGNl+64m8VBrETkdGCDBdOsCr3F3jg==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^7.0.0", + "mongodb-connection-string-url": "^7.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.806.0", + "@mongodb-js/zstd": "^7.0.0", + "gcp-metadata": "^7.0.1", + "kerberos": "^7.0.0", + "mongodb-client-encryption": ">=7.0.0 <7.1.0", + "snappy": "^7.3.2", + "socks": "^2.8.6" + }, + "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": "7.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.0.tgz", + "integrity": "sha512-irhhjRVLE20hbkRl4zpAYLnDMM+zIZnp0IDB9akAFFUZp/3XdOfwwddc7y6cNvF2WCEtfTYRwYbIfYa2kVY0og==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^13.0.0", + "whatwg-url": "^14.1.0" + }, + "engines": { + "node": ">=20.19.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/Week4/homework/package.json b/Week4/homework/package.json new file mode 100644 index 000000000..35ad23c09 --- /dev/null +++ b/Week4/homework/package.json @@ -0,0 +1,16 @@ +{ + "name": "database-assignment-week4", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "dependencies": { + "mongodb": "^7.0.0" + } +} diff --git a/Week4/index.js b/Week4/index.js new file mode 100644 index 000000000..2952bda65 --- /dev/null +++ b/Week4/index.js @@ -0,0 +1,84 @@ + +const { MongoClient } = require("mongodb"); +const uri = "mongodb://127.0.0.1:27017"; +const client = new MongoClient(uri); + +async function getPopulationByCountryPerYear(countryName) { + const db = client.db("databaseWeek4"); + const collection = db.collection("population"); + + const result = await collection + .aggregate([ + + { $match: { Country: countryName } }, + + { + $group: { + _id: "$Year", + countPopulation: { + $sum: { + $add: [ + { $toLong: "$M" }, + { $toLong: "$F" }, + ], + }, + }, + }, + }, + + { $sort: { _id: 1 } }, + ]) + .toArray(); + + return result; +} + +async function getContinentsForYearAndAge(year, age) { + const db = client.db("databaseWeek4"); + const collection = db.collection("population"); + + const result = await collection + .aggregate([ + { + $match: { + Year: year, + Age: age, + }, + }, + + { + $addFields: { + TotalPopulation: { + $add: [ + { $toLong: "$M" }, + { $toLong: "$F" }, + ], + }, + }, + }, + ]) + .toArray(); + + return result; +} + +async function main() { + try { + await client.connect(); + console.log("Connected to MongoDB"); + + const byYear = await getPopulationByCountryPerYear("Netherlands"); + console.log("Population of Netherlands per year:"); + console.log(byYear); + + const continents = await getContinentsForYearAndAge(2020, "100+"); + console.log(" Continents for 2020, Age 100+ :"); + console.log(continents); + } catch (err) { + console.error(err); + } finally { + await client.close(); + } +} + +main();