diff --git a/LICENSE b/LICENSE index 261eeb9..6fad4e7 100644 --- a/LICENSE +++ b/LICENSE @@ -175,18 +175,7 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] + Copyright 2021 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -198,4 +187,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. \ No newline at end of file diff --git a/aggregation.js b/aggregation.js index 32baad8..eaa47b7 100644 --- a/aggregation.js +++ b/aggregation.js @@ -10,6 +10,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); diff --git a/changeStreams.js b/changeStreams.js index 277c897..2902b10 100644 --- a/changeStreams.js +++ b/changeStreams.js @@ -11,6 +11,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); @@ -137,7 +140,7 @@ async function monitorListingsUsingStreamAPI(client, timeInMs = 60000, pipeline // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#watch for the watch() docs const changeStream = collection.watch(pipeline); - // See https://mongodb.github.io/node-mongodb-native/3.6/api/ChangeStream.html#pipe for the pipe() docs + // See https://mongodb.github.io/node-mongodb-native/3.6/api/ChangeStream.html#stream for the stream() docs changeStream.stream().pipe( new stream.Writable({ objectMode: true, diff --git a/changeStreamsTestData.js b/changeStreamsTestData.js index ecc774d..7867494 100644 --- a/changeStreamsTestData.js +++ b/changeStreamsTestData.js @@ -14,6 +14,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); diff --git a/connection.js b/connection.js index f504282..85a9e12 100644 --- a/connection.js +++ b/connection.js @@ -10,6 +10,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); diff --git a/create.js b/create.js index 6ad1fc8..d1d302c 100644 --- a/create.js +++ b/create.js @@ -10,6 +10,9 @@ async function main(){ /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); diff --git a/delete.js b/delete.js index fca8743..32b9646 100644 --- a/delete.js +++ b/delete.js @@ -10,6 +10,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); diff --git a/read.js b/read.js index 53b6568..91fc423 100644 --- a/read.js +++ b/read.js @@ -10,6 +10,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); @@ -89,14 +92,14 @@ async function findListingsWithMinimumBedroomsBathroomsAndMostRecentReviews(clie if (results.length > 0) { console.log(`Found listing(s) with at least ${minimumNumberOfBedrooms} bedrooms and ${minimumNumberOfBathrooms} bathrooms:`); results.forEach((result, i) => { - date = new Date(result.last_review).toDateString(); + const date = new Date(result.last_review).toDateString(); console.log(); console.log(`${i + 1}. name: ${result.name}`); console.log(` _id: ${result._id}`); console.log(` bedrooms: ${result.bedrooms}`); console.log(` bathrooms: ${result.bathrooms}`); - console.log(` most recent review date: ${new Date(result.last_review).toDateString()}`); + console.log(` most recent review date: ${date}`); }); } else { console.log(`No listings found with at least ${minimumNumberOfBedrooms} bedrooms and ${minimumNumberOfBathrooms} bathrooms`); diff --git a/template.js b/template.js index 39ab3e8..2785d10 100644 --- a/template.js +++ b/template.js @@ -10,6 +10,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); diff --git a/transaction-bankingexample.js b/transaction-bankingexample.js new file mode 100644 index 0000000..9ed5479 --- /dev/null +++ b/transaction-bankingexample.js @@ -0,0 +1,128 @@ +const { MongoClient } = require('mongodb'); + +// In MongoDB 4.2 and earlier, CRUD operations in transactions must be on existing collections +// See https://docs.mongodb.com/manual/core/transactions/#transactions-api for more information + +// Before running this script... +// 1. Create a database named 'banking' +// 2. Create a collection named 'accounts' in the database +// 3. Create two documents in the 'accounts' collection: +// {"_id":"account1", "balance":500} +// {"_id":"account2", "balance":0} +// 4: Optional: add schema validation to ensure an account balance cannot drop below 0. +// See https://docs.mongodb.com/manual/core/schema-validation/ for details on how to +// enable schema validation. Configuring schema validation in MongoDB Compass is an +// easy way to add schema validation to an existing database: https://docs.mongodb.com/compass/current/validation/ +// +// { +// $jsonSchema: { +// properties: { +// balance: { +// minimum: 0, +// description: 'account balance cannot be negative' +// } +// } +// } +// } + +async function main() { + /** + * Connection URI. Update , , and to reflect your cluster. + * See https://docs.mongodb.com/drivers/node/ for more details + */ + const uri = "mongodb+srv://:@/banking?retryWrites=true&w=majority"; + + /** + * The Mongo Client you will use to interact with your database + * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + */ + const client = new MongoClient(uri); + + try { + // Connect to the MongoDB cluster + await client.connect(); + + // Transfer $100 from "account1" to "account2" + await transferMoney(client, "account1", "account2", 100); + + } finally { + // Close the connection to the MongoDB cluster + await client.close(); + } +} + +main().catch(console.error); + +/** + * Transfer money from one bank account to another using + * @param {MongoClient} client A MongoClient that is connected to a cluster with the banking database + * @param {String} account1 The _id of the account where money should be subtracted + * @param {String} account2 The _id of the account where money should be added + * @param {Number} amount The amount of money to be transferred + */ +async function transferMoney(client, account1, account2, amount) { + + /** + * The accounts collection in the banking database + */ + const accountsCollection = client.db("banking").collection("accounts"); + + // Step 1: Start a Client Session + // See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html#startSession for the startSession() docs + const session = client.startSession(); + + // Step 2: Optional. Define options for the transaction + const transactionOptions = { + readPreference: 'primary', + readConcern: { level: 'local' }, + writeConcern: { w: 'majority' } + }; + + try { + // Step 3: Use withTransaction to start a transaction, execute the callback, and commit (or abort on error) + // Note: The callback for withTransaction MUST be async and/or return a Promise. + // See https://mongodb.github.io/node-mongodb-native/3.6/api/ClientSession.html#withTransaction for the withTransaction() docs + const transactionResults = await session.withTransaction(async () => { + + // Important:: You must pass the session to each of the operations + + // Remove the money from the first account + const subtractMoneyResults = await accountsCollection.updateOne( + { _id: account1 }, + { $inc: { balance: amount * -1 } }, + { session }); + console.log(`${subtractMoneyResults.matchedCount} document(s) found in the accounts collection with _id ${account1}.`); + console.log(`${subtractMoneyResults.modifiedCount} document(s) was/were updated to remove the money.`); + if (subtractMoneyResults.modifiedCount !== 1) { + await session.abortTransaction(); + return; + } + + // Add the money to the second account + const addMoneyResults = await accountsCollection.updateOne( + { _id: account2 }, + { $inc: { balance: amount } }, + { session }); + console.log(`${addMoneyResults.matchedCount} document(s) found in the accounts collection with _id ${account2}.`); + console.log(`${addMoneyResults.modifiedCount} document(s) was/were updated to add the money.`); + if (addMoneyResults.modifiedCount !== 1) { + await session.abortTransaction(); + return; + } + + }, transactionOptions); + + if (transactionResults) { + console.log("The money was successfully transferred. Database operations from the transaction are now visible outside the transaction."); + } else { + console.log("The money was not transferred. The transaction was intentionally aborted."); + } + } catch (e) { + console.log("The money was not transferred. The transaction was aborted due to an unexpected error: " + e); + } finally { + // Step 4: End the session + await session.endSession(); + } + +} + diff --git a/transaction-inventoryexample.js b/transaction-inventoryexample.js new file mode 100644 index 0000000..42505b1 --- /dev/null +++ b/transaction-inventoryexample.js @@ -0,0 +1,127 @@ +const { MongoClient } = require('mongodb'); + +// In MongoDB 4.2 and earlier, CRUD operations in transactions must be on existing collections +// See https://docs.mongodb.com/manual/core/transactions/#transactions-api for more information + +// Before running this script... +// 1. Create a database named 'book-store' +// 2. Create a collection named 'orders' in the database +// 3. Create a collection named 'inventory' in the database +// 3. Create a document in the 'inventory' collection: +// { "_id": "parks-rec-book", "name": "The Ultimate Parks and Rec Book for the Ultimate Fans", "numberInStock": 5 } +// 4: Optional: Add schema validation to the 'inventory' collection to ensure the number of items in stock cannot drop below 0. +// See https://docs.mongodb.com/manual/core/schema-validation/ for details on how to +// enable schema validation. Configuring schema validation in MongoDB Compass is an +// easy way to add schema validation to an existing database: https://docs.mongodb.com/compass/current/validation/ +// +// { +// $jsonSchema: { +// properties: { +// numberInStock: { +// minimum: 0, +// description: 'numberInStock cannot be negative' +// } +// } +// } +// } + +async function main() { + /** + * Connection URI. Update , , and to reflect your cluster. + * See https://docs.mongodb.com/drivers/node/ for more details + */ + const uri = "mongodb+srv://:@/book-store?retryWrites=true&w=majority"; + + /** + * The Mongo Client you will use to interact with your database + * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + */ + const client = new MongoClient(uri); + + try { + // Connect to the MongoDB cluster + await client.connect(); + + // User1 purchases 1 copy of parks-rec-book + await purchaseBook(client, "User1", "parks-rec-book", 1, "paid"); + + } finally { + // Close the connection to the MongoDB cluster + await client.close(); + } +} + +main().catch(console.error); + +/** + * Purchase a book + * @param {MongoClient} client A MongoClient that is connected to a cluster with the book-store database + * @param {String} userId The _id of the user who is purchasing the book + * @param {String} bookId The _id of the book being purchased + * @param {Number} quantity The number of copies being purchased + * @param {String} status The order status + */ +async function purchaseBook(client, userId, bookId, quantity, status) { + + /** + * The orders collection in the book-store database + */ + const ordersCollection = client.db("book-store").collection("orders"); + + /** + * The inventory collection in the book-store database + */ + const inventoryCollection = client.db("book-store").collection("inventory"); + + // Step 1: Start a Client Session + // See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html#startSession for the startSession() docs + const session = client.startSession(); + + // Step 2: Optional. Define options for the transaction + const transactionOptions = { + readPreference: 'primary', + readConcern: { level: 'local' }, + writeConcern: { w: 'majority' } + }; + + try { + // Step 3: Use withTransaction to start a transaction, execute the callback, and commit (or abort on error) + // Note: The callback for withTransaction MUST be async and/or return a Promise. + // See https://mongodb.github.io/node-mongodb-native/3.6/api/ClientSession.html#withTransaction for the withTransaction() docs + const transactionResults = await session.withTransaction(async () => { + + // Important:: You must pass the session to each of the operations + + // Update the inventory to reflect the book has been sold + const updateInventoryResults = await inventoryCollection.updateOne( + { _id: bookId }, + { $inc: { numberInStock: quantity * -1 } }, + { session }); + console.log(`${updateInventoryResults.matchedCount} document(s) found in the inventory collection with _id ${bookId}.`); + console.log(`${updateInventoryResults.modifiedCount} document(s) was/were updated.`); + if (updateInventoryResults.modifiedCount !== 1) { + await session.abortTransaction(); + return; + } + + // Record the order in the orders collection + const insertOrderResults = await ordersCollection.insertOne( + { "userId": userId , bookId: bookId, quantity: quantity, status: status }, + { session }); + console.log(`New order recorded with the following id: ${insertOrderResults.insertedId}`); + + }, transactionOptions); + + if (transactionResults) { + console.log("The order was successfully processed. Database operations from the transaction are now visible outside the transaction."); + } else { + console.log("The order was not successful. The transaction was intentionally aborted."); + } + } catch (e) { + console.log("The order was not successful. The transaction was aborted due to an unexpected error: " + e); + } finally { + // Step 4: End the session + await session.endSession(); + } + +} diff --git a/transaction.js b/transaction.js index 754e11e..33ff107 100644 --- a/transaction.js +++ b/transaction.js @@ -14,6 +14,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); diff --git a/update.js b/update.js index b107c55..e82e894 100644 --- a/update.js +++ b/update.js @@ -10,6 +10,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri); diff --git a/usersCollection.js b/usersCollection.js index 4f6250c..ba492eb 100644 --- a/usersCollection.js +++ b/usersCollection.js @@ -20,6 +20,9 @@ async function main() { /** * The Mongo Client you will use to interact with your database * See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details + * In case: '[MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated...' + * pass option { useUnifiedTopology: true } to the MongoClient constructor. + * const client = new MongoClient(uri, {useUnifiedTopology: true}) */ const client = new MongoClient(uri);