diff --git a/Week4/homework/ex1-aggregation/aggregation.js b/Week4/homework/ex1-aggregation/aggregation.js new file mode 100644 index 000000000..40e17cdc8 --- /dev/null +++ b/Week4/homework/ex1-aggregation/aggregation.js @@ -0,0 +1,110 @@ +const { MongoClient, ServerApiVersion } = require("mongodb"); +require("dotenv").config(); +const uri = process.env.MONGODB_URL; + + +const client = new MongoClient(uri, { + serverApi: { + version: ServerApiVersion.v1, + strict: true, + deprecationErrors: true, + }, +}); + +async function run() { + try { + await client.connect(); + console.log("Connected to MongoDB successfully!"); + + const db = client.db("databaseWeek4"); + const collection = db.collection("population"); + // ===================== + // 1️⃣ Get population per country (example: Netherlands) + // ===================== + const populationNetherlands = await getPopulationPerCountry( + collection, + "Netherlands" + ); + console.log("\nPopulation per year for Netherlands:"); + console.table(populationNetherlands); + + // ===================== + // 2️ Get population per continent for year & age + // ===================== + const continentInfo2020 = await getContinentInfo(collection, 2020, "100+"); + console.log("\nPopulation for continents in 2020 (age 100+):"); + console.table(continentInfo2020); + } catch (err) { + console.error(err); + } finally { + await client.close(); + console.log("MongoDB connection closed."); + } +} +run(); + +// ===================== +// D1: Total population per country +// ===================== +const getPopulationPerCountry = async (collection, country) => { + const pipeline = [ + { $match: { Country: country } }, + { + $group: { + _id: "$Year", + countPopulation: { $sum: { $add: ["$M", "$F"] } }, + }, + }, + { + $project: { + _id: 0, + Year: "$_id", + countPopulation: 1, + }, + }, + { $sort: { Year: 1 } }, + ]; + + return await collection.aggregate(pipeline).toArray(); +}; + +// D2: Total population per continent/year/age + +const getContinentInfo = async (collection, year, age) => { + const pipeline = [ + { + $match: { + Country: { + $in: [ + "AFRICA", + "ASIA", + "EUROPE", + "LATIN AMERICA AND THE CARIBBEAN", + "NORTHERN AMERICA", + "OCEANIA", + ], + }, + Year: year, + Age: age, + }, + }, + { + $addFields: { + TotalPopulation: { $add: ["$M", "$F"] }, + }, + }, + { + $project: { + _id: 0, + Country: 1, + Year: 1, + Age: 1, + M: 1, + F: 1, + TotalPopulation: 1, + }, + }, + ]; + + return await collection.aggregate(pipeline).toArray(); +}; diff --git a/Week4/homework/ex2-transactions/index.js b/Week4/homework/ex2-transactions/index.js new file mode 100644 index 000000000..70f70efc1 --- /dev/null +++ b/Week4/homework/ex2-transactions/index.js @@ -0,0 +1,13 @@ +const { setupAccounts } = require("./setup"); +const { transferMoney } = require("./transfer"); + +async function main() { + + await setupAccounts(); + + await transferMoney(101, 102, 1000, "Homework test"); + + console.log("All operations completed!"); +} + +main(); diff --git a/Week4/homework/ex2-transactions/setup.js b/Week4/homework/ex2-transactions/setup.js new file mode 100644 index 000000000..b0f9fb307 --- /dev/null +++ b/Week4/homework/ex2-transactions/setup.js @@ -0,0 +1,39 @@ +const { MongoClient, ServerApiVersion } = require("mongodb"); +require("dotenv").config(); + +const uri = process.env.MONGODB_URL; +const client = new MongoClient(uri, { + serverApi: { + version: ServerApiVersion.v1, + strict: true, + deprecationErrors: true, + }, +}); + +async function setupAccounts() { + try { + await client.connect(); + const db = client.db("databaseWeek4"); + const collection = db.collection("accounts"); + + await collection.deleteMany({}); + + const accounts = [ + { account_number: 101, balance: 5000, account_changes: [] }, + { account_number: 102, balance: 3000, account_changes: [] } + ]; + + await collection.insertMany(accounts); + console.log("Accounts setup completed!"); + } catch (err) { + console.error(err); + } finally { + await client.close(); + } +} + +module.exports = { setupAccounts }; + +if (require.main === module) { + setupAccounts(); +} diff --git a/Week4/homework/ex2-transactions/transfer.js b/Week4/homework/ex2-transactions/transfer.js new file mode 100644 index 000000000..9162b340c --- /dev/null +++ b/Week4/homework/ex2-transactions/transfer.js @@ -0,0 +1,75 @@ +const { MongoClient, ServerApiVersion } = require("mongodb"); +require("dotenv").config(); + +const uri = process.env.MONGODB_URL; +const client = new MongoClient(uri, { + serverApi: { + version: ServerApiVersion.v1, + strict: true, + deprecationErrors: true, + }, +}); + +async function transferMoney(fromAccount, toAccount, amount, remark) { + const session = client.startSession(); + try { + await client.connect(); + const db = client.db("databaseWeek4"); + const collection = db.collection("accounts"); + + await session.withTransaction(async () => { + + const from = await collection.findOne({ account_number: fromAccount }, { session }); + const to = await collection.findOne({ account_number: toAccount }, { session }); + + if (!from || !to) throw new Error("Account not found"); + if (from.balance < amount) throw new Error("Insufficient funds"); + + await collection.updateOne( + { account_number: fromAccount }, + { + $inc: { balance: -amount }, + $push: { + account_changes: { + change_number: from.account_changes.length + 1, + amount: -amount, + changed_date: new Date(), + remark: remark + } + } + }, + { session } + ); + + await collection.updateOne( + { account_number: toAccount }, + { + $inc: { balance: amount }, + $push: { + account_changes: { + change_number: to.account_changes.length + 1, + amount: amount, + changed_date: new Date(), + remark: remark + } + } + }, + { session } + ); + }); + + console.log(`Transferred ${amount} from ${fromAccount} to ${toAccount}`); + } catch (err) { + console.error("Transaction failed:", err.message); + } finally { + await session.endSession(); + await client.close(); + } +} + +module.exports = { transferMoney }; + + +if (require.main === module) { + transferMoney(101, 102, 1000, "Test transfer"); +} diff --git a/Week4/homework/package-lock.json b/Week4/homework/package-lock.json new file mode 100644 index 000000000..2ab4cf2c9 --- /dev/null +++ b/Week4/homework/package-lock.json @@ -0,0 +1,176 @@ +{ + "name": "homework", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "homework", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "csv-parser": "^3.2.0", + "mongodb": "^6.21.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.4.tgz", + "integrity": "sha512-p7X/ytJDIdwUfFL/CLOhKgdfJe1Fa8uw9seJYvdOmnP9JBWGWHW69HkOixXS6Wy9yvGf1MbhcS6lVmrhy4jm2g==", + "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/csv-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.2.0.tgz", + "integrity": "sha512-fgKbp+AJbn1h2dcAHKIdKNSSjfp43BZZykXsCjzALjKy80VXQNHPFJ6T9Afwdzoj24aMkq8GwDS7KGcDPpejrA==", + "license": "MIT", + "bin": { + "csv-parser": "bin/csv-parser" + }, + "engines": { + "node": ">= 10" + } + }, + "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.21.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.21.0.tgz", + "integrity": "sha512-URyb/VXMjJ4da46OeSXg+puO39XH9DeQpWCslifrRn9JWugy0D+DvvBvkm2WxmHe61O/H19JM66p1z7RHVkZ6A==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" + }, + "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/Week4/homework/package.json b/Week4/homework/package.json new file mode 100644 index 000000000..7770c89a6 --- /dev/null +++ b/Week4/homework/package.json @@ -0,0 +1,16 @@ +{ + "name": "homework", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "csv-parser": "^3.2.0", + "mongodb": "^6.21.0" + } +}