-
Notifications
You must be signed in to change notification settings - Fork 240
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add script to create new users Working transaction Add abort transaction Added logging and doc Add docs Add unique index Formatting and typos Update sample user names Add session to findOne()
- Loading branch information
Showing
2 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
const { MongoClient } = require('mongodb'); | ||
|
||
// CRUD operations in transactions must be on existing collections, so be sure you have run | ||
// usersCollection.js prior to running this script. | ||
|
||
async function main() { | ||
/** | ||
* Connection URI. Update <username>, <password>, and <your-cluster-url> to reflect your cluster. | ||
* See http://bit.ly/NodeDocs_lauren for more details | ||
*/ | ||
const uri = "mongodb+srv://<username>:<password>@<your-cluster-url>/test?retryWrites=true&w=majority"; | ||
|
||
/** | ||
* The Mongo Client you will use to interact with your database | ||
* See bit.ly/Node_MongoClient for more details | ||
*/ | ||
const client = new MongoClient(uri); | ||
|
||
try { | ||
// Connect to the MongoDB cluster | ||
await client.connect(); | ||
|
||
// Make the appropriate DB calls | ||
|
||
await createReservation(client, | ||
"leslie@example.com", | ||
"Infinite Views", | ||
[new Date("2019-12-31"), new Date("2020-01-01")], | ||
{ pricePerNight: 180, specialRequests: "Late checkout", breakfastIncluded: true }); | ||
|
||
} finally { | ||
// Close the connection to the MongoDB cluster | ||
await client.close(); | ||
} | ||
} | ||
|
||
main().catch(console.error); | ||
|
||
/** | ||
* Create a reservation by storing information in both the users collection and the listingsAndReviews collection | ||
* Note: this function assumes there is only one Airbnb listing in the collection with the given name. If more than | ||
* listing exists with the given name, a reservation will be created for the first listing the database finds. | ||
* @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database | ||
* @param {String} userEmail The email address of the user who is creating the reservation | ||
* @param {String} nameOfListing The name of the Airbnb listing to be reserved | ||
* @param {Array.Date} reservationDates An array of the date(s) for the reservation | ||
* @param {Object} reservationDetails An object containing additional reservation details that need to be stored with the reservation | ||
*/ | ||
async function createReservation(client, userEmail, nameOfListing, reservationDates, reservationDetails) { | ||
|
||
/** | ||
* The users collection in the sample_airbnb database | ||
*/ | ||
const usersCollection = client.db("sample_airbnb").collection("users"); | ||
|
||
/** | ||
* The listingsAndReviews collection in the sample_airbnb database | ||
*/ | ||
const listingsAndReviewsCollection = client.db("sample_airbnb").collection("listingsAndReviews"); | ||
|
||
/** | ||
* The reservation document that will be added to the users collection document for this user | ||
*/ | ||
const reservation = createReservationDocument(nameOfListing, reservationDates, reservationDetails); | ||
|
||
// Step 1: Start a Client Session | ||
// See http://bit.ly/Node_startSession for the startSession() docs | ||
const session = client.startSession(); | ||
|
||
// Step 2: Optional. Define options to use 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 http://bit.ly/Node_withTransaction for the withTransaction() docs | ||
const transactionResults = await session.withTransaction(async () => { | ||
|
||
// Important:: You must pass the session to each of the operations | ||
|
||
// Add a reservation to the reservations array for the appropriate document in the users collection | ||
const usersUpdateResults = await usersCollection.updateOne( | ||
{ email: userEmail }, | ||
{ $addToSet: { reservations: reservation } }, | ||
{ session }); | ||
console.log(`${usersUpdateResults.matchedCount} document(s) found in the users collection with the email address ${userEmail}.`) | ||
console.log(`${usersUpdateResults.modifiedCount} document(s) was/were updated to include the reservation.`); | ||
|
||
// Check if the Airbnb listing is already reserved for those dates. If so, abort the transaction. | ||
const isListingReservedResults = await listingsAndReviewsCollection.findOne( | ||
{ name: nameOfListing, datesReserved: { $in: reservationDates } }, | ||
{ session }); | ||
if (isListingReservedResults) { | ||
await session.abortTransaction(); | ||
console.error("This listing is already reserved for at least one of the given dates. The reservation could not be created."); | ||
console.error("Any operations that already occurred as part of this transaction will be rolled back.") | ||
return; | ||
} | ||
|
||
// Add the reservation dates to the datesReserved array for the appropriate document in the listingsAndRewiews collection | ||
const listingsAndReviewsUpdateResults = await listingsAndReviewsCollection.updateOne( | ||
{ name: nameOfListing }, | ||
{ $addToSet: { datesReserved: { $each: reservationDates } } }, | ||
{ session }); | ||
console.log(`${listingsAndReviewsUpdateResults.matchedCount} document(s) found in the listingsAndReviews collection with the name ${nameOfListing}.`) | ||
console.log(`${listingsAndReviewsUpdateResults.modifiedCount} document(s) was/were updated to include the reservation dates.`); | ||
|
||
}, transactionOptions); | ||
|
||
// TODO: I couldn't find documentation that explicitly said that the transactionResults will be undefined if the transaction is | ||
// unsuccessful, but that seems to be the case. Can you confirm this is true? | ||
if (transactionResults) { | ||
console.log("The reservation was successfully created."); | ||
} else { | ||
console.log("An error occurred and the reservation could not be created."); | ||
} | ||
} finally { | ||
// Step 4: End the session | ||
await session.endSession(); | ||
} | ||
|
||
} | ||
|
||
/** | ||
* A helper function that generates a reservation document | ||
* @param {String} nameOfListing The name of the Airbnb listing to be reserved | ||
* @param {Array.Date} reservationDates An array of the date(s) for the reservation | ||
* @param {Object} reservationDetails An object containing additional reservation details that need to be stored with the reservation | ||
* @returns {Object} The reservation document | ||
*/ | ||
function createReservationDocument(nameOfListing, reservationDates, reservationDetails) { | ||
// Create the reservation | ||
let reservation = { | ||
name: nameOfListing, | ||
dates: reservationDates, | ||
} | ||
|
||
// Add additional properties from reservationDetails to the reservation | ||
for (let detail in reservationDetails) { | ||
reservation[detail] = reservationDetails[detail]; | ||
} | ||
|
||
return reservation; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
const { MongoClient } = require('mongodb'); | ||
|
||
/** | ||
* This script creates 3 new users in the users collection in the sample_airbnb database. | ||
* The users collection does not need to exist before running this script. | ||
* This script also creates a unique index on the email field in the users collection. | ||
* | ||
* You will see "duplicate key" errors if you attempt to run this script more than once | ||
* without dropping the documents in the users collection, because the unique index will | ||
* not allow you to insert more than one document into the collection with the same email address. | ||
*/ | ||
|
||
async function main() { | ||
/** | ||
* Connection URI. Update <username>, <password>, and <your-cluster-url> to reflect your cluster. | ||
* See http://bit.ly/NodeDocs_lauren for more details | ||
*/ | ||
const uri = "mongodb+srv://<username>:<password>@<your-cluster-url>/test?retryWrites=true&w=majority"; | ||
|
||
/** | ||
* The Mongo Client you will use to interact with your database | ||
* See bit.ly/Node_MongoClient for more details | ||
*/ | ||
const client = new MongoClient(uri); | ||
|
||
try { | ||
// Connect to the MongoDB cluster | ||
await client.connect(); | ||
|
||
// Make the appropriate DB calls | ||
|
||
// Create 3 new users in the users collection | ||
await createMultipleUsers(client, [ | ||
{ | ||
email: "leslie@example.com", | ||
name: "Leslie Yepp" | ||
}, | ||
{ | ||
email: "april@example.com", | ||
name: "April Ludfence" | ||
}, | ||
{ | ||
email: "tom@example.com", | ||
name: "Tom Haverdodge" | ||
} | ||
]); | ||
|
||
// Create a unique index on the email field in the users collection. | ||
// Note that if you run this script when you already have duplicate emails in the user collection, | ||
// MongoDB will be unable to create the unique index. | ||
const createIndexResults = await client.db("sample_airbnb").collection("users").createIndex({ "email": 1 }, { unique: true }); | ||
console.log(`Index successfully created: ${createIndexResults}`); | ||
|
||
} finally { | ||
// Close the connection to the MongoDB cluster | ||
await client.close(); | ||
} | ||
} | ||
|
||
main().catch(console.error); | ||
|
||
/** | ||
* Create multiple users | ||
* @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database | ||
* @param {Object[]} newUsers The new users to be added | ||
*/ | ||
async function createMultipleUsers(client, newUsers) { | ||
// See http://bit.ly/Node_InsertMany for the insertMany() docs | ||
const result = await client.db("sample_airbnb").collection("users").insertMany(newUsers); | ||
|
||
console.log(`${result.insertedCount} new user(s) created with the following id(s):`); | ||
console.log(result.insertedIds); | ||
} |