diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..90f0958 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +--- +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + assignees: [b1ron] + open-pull-requests-limit: 5 + schedule: + interval: "weekly" + day: "tuesday" + time: "01:32" diff --git a/README.md b/README.md index 95a22ea..24d0fa9 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,10 @@ # Quick Start: Node.js and MongoDB -This repository will walk you through the process of quickly getting started using Node.js and MongoDB together. +This is a fork of https://github.com/FerretDB/nodejs-example, simplified to run with FerretDB. -This branch uses MongoDB 4.4, MongoDB Node.js Driver 3.6.4, and Node.js 14.15.4. To see an earlier version that uses MongoDB 4.0, MongoDB Node.js Driver 3.3.2, and Node.js 10.16.3, visit the [3.3.2 branch](https://github.com/mongodb-developer/nodejs-quickstart/tree/3.3.2). +### Execution Steps -## Topics - -* How to get connected to your database: [blog post](https://developer.mongodb.com/quickstart/node-connect-mongodb/) | [code example](connection.js) -* Creating documents: [blog post](https://developer.mongodb.com/quickstart/node-crud-tutorial/) | [code example](create.js) -* Reading documents: [blog post](https://developer.mongodb.com/quickstart/node-crud-tutorial/) | [code example](read.js) -* Updating documents: [blog post](https://developer.mongodb.com/quickstart/node-crud-tutorial/) | [code example](update.js) -* Deleting documents: [blog post](https://developer.mongodb.com/quickstart/node-crud-tutorial/) | [code example](delete.js) -* The aggregation framework: [blog post](https://developer.mongodb.com/quickstart/node-aggregation-framework/) | [code example](aggregation.js) -* Transactions: [blog post](https://developer.mongodb.com/quickstart/node-transactions/) | [code example](transaction.js) -* Change streams: [blog post](https://developer.mongodb.com/quickstart/nodejs-change-streams-triggers/) | [code example](changeStreams.js) - -## Related Videos - -[How to Perform the CRUD Operations Using MongoDB & Node.js](https://youtu.be/ayNI9Q84v8g) - -## Questions? - -Questions about this repo or how to use MongoDB and Node.js together? Ask them in the [MongoDB Community](https://community.mongodb.com). +1. `npm install` +2. `node index.js --uri='mongodb://localhost:27017/'` +3. To run with strict Stable API: `node index.js --uri='mongodb://localhost:27017/' --strict` +4. To run with PLAIN authentication pass PLAIN to the `authMechanism` URI option: `node index.js --uri='mongodb://localhost:27017/?authMechanism=PLAIN'` diff --git a/aggregation.js b/aggregation.js deleted file mode 100644 index eaa47b7..0000000 --- a/aggregation.js +++ /dev/null @@ -1,78 +0,0 @@ -const { MongoClient } = require('mongodb'); - -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://:@/sample_airbnb?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 - * 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); - - try { - // Connect to the MongoDB cluster - await client.connect(); - - // Make the appropriate DB calls - - // Print the 10 cheapest suburbs in the Sydney, Australia market - await printCheapestSuburbs(client, "Australia", "Sydney", 10); - - } finally { - // Close the connection to the MongoDB cluster - await client.close(); - } -} - -main().catch(console.error); - -/** - * Print the cheapest suburbs for a given market - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {String} country The country for the given market - * @param {String} market The market you want to search - * @param {number} maxNumberToPrint The maximum number of suburbs to print - */ -async function printCheapestSuburbs(client, country, market, maxNumberToPrint) { - const pipeline = [ - { - '$match': { - 'bedrooms': 1, - 'address.country': country, - 'address.market': market, - 'address.suburb': { - '$exists': 1, - '$ne': '' - }, - 'room_type': 'Entire home/apt' - } - }, { - '$group': { - '_id': '$address.suburb', - 'averagePrice': { - '$avg': '$price' - } - } - }, { - '$sort': { - 'averagePrice': 1 - } - }, { - '$limit': maxNumberToPrint - } - ]; - - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#aggregate for the aggregate() docs - const aggCursor = client.db("sample_airbnb").collection("listingsAndReviews").aggregate(pipeline); - - await aggCursor.forEach(airbnbListing => { - console.log(`${airbnbListing._id}: ${airbnbListing.averagePrice}`); - }); -} diff --git a/changeStreams.js b/changeStreams.js deleted file mode 100644 index 2902b10..0000000 --- a/changeStreams.js +++ /dev/null @@ -1,156 +0,0 @@ -const { MongoClient } = require('mongodb'); -const stream = require('stream'); - -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://:@/sample_airbnb?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 - * 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); - - try { - // Connect to the MongoDB cluster - await client.connect(); - - // Make the appropriate DB calls - - /** - * An aggregation pipeline that matches on new listings in the country of Australia and the Sydney market - */ - const pipeline = [ - { - '$match': { - 'operationType': 'insert', - 'fullDocument.address.country': 'Australia', - 'fullDocument.address.market': 'Sydney' - } - } - ]; - - // This script contains three ways to monitor new listings in the listingsAndReviews collection. - // Comment in the monitoring function you'd like to use. - - // OPTION ONE: Monitor new listings using EventEmitter's on() function. - // await monitorListingsUsingEventEmitter(client, 30000, pipeline); - - // OPTION TWO: Monitor new listings using ChangeStream's hasNext() function - // await monitorListingsUsingHasNext(client, 30000, pipeline); - - // OPTION THREE: Monitor new listings using the Stream API - // await monitorListingsUsingStreamAPI(client, 30000, pipeline); - - } finally { - // Close the connection to the MongoDB cluster - await client.close(); - } -} - -main().catch(console.error); - -/** - * Close the given change stream after the given amount of time - * @param {*} timeInMs The amount of time in ms to monitor listings - * @param {*} changeStream The open change stream that should be closed - */ -function closeChangeStream(timeInMs = 60000, changeStream) { - return new Promise((resolve) => { - setTimeout(() => { - console.log("Closing the change stream"); - changeStream.close(); - resolve(); - }, timeInMs) - }) -}; - -/** - * Monitor listings in the listingsAndReviews collections for changes - * This function uses the on() function from the EventEmitter class to monitor changes - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {Number} timeInMs The amount of time in ms to monitor listings - * @param {Object} pipeline An aggregation pipeline that determines which change events should be output to the console - */ -async function monitorListingsUsingEventEmitter(client, timeInMs = 60000, pipeline = []) { - const collection = client.db("sample_airbnb").collection("listingsAndReviews"); - - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#watch for the watch() docs - const changeStream = collection.watch(pipeline); - - // ChangeStream inherits from the Node Built-in Class EventEmitter (https://nodejs.org/dist/latest-v12.x/docs/api/events.html#events_class_eventemitter). - // We can use EventEmitter's on() to add a listener function that will be called whenever a change occurs in the change stream. - // See https://nodejs.org/dist/latest-v12.x/docs/api/events.html#events_emitter_on_eventname_listener for the on() docs. - changeStream.on('change', (next) => { - console.log(next); - }); - - // Wait the given amount of time and then close the change stream - await closeChangeStream(timeInMs, changeStream); -} - -/** - * Monitor listings in the listingsAndReviews collections for changes - * This function uses the hasNext() function from the MongoDB Node.js Driver's ChangeStream class to monitor changes - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {Number} timeInMs The amount of time in ms to monitor listings - * @param {Object} pipeline An aggregation pipeline that determines which change events should be output to the console - */ -async function monitorListingsUsingHasNext(client, timeInMs = 60000, pipeline = []) { - const collection = client.db("sample_airbnb").collection("listingsAndReviews"); - - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#watch for the watch() docs - const changeStream = collection.watch(pipeline); - - // Set a timer that will close the change stream after the given amount of time - // Function execution will continue because we are not using "await" here - closeChangeStream(timeInMs, changeStream); - - // We can use ChangeStream's hasNext() function to wait for a new change in the change stream. - // See https://mongodb.github.io/node-mongodb-native/3.6/api/ChangeStream.html for the ChangeStream docs. - try { - while (await changeStream.hasNext()) { - console.log(await changeStream.next()); - } - } catch (error) { - if (changeStream.isClosed()) { - console.log("The change stream is closed. Will not wait on any more changes.") - } else { - throw error; - } - } -} - -/** - * Monitor listings in the listingsAndReviews collection for changes - * This function uses the Stream API (https://nodejs.org/api/stream.html) to monitor changes - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {Number} timeInMs The amount of time in ms to monitor listings - * @param {Object} pipeline An aggregation pipeline that determines which change events should be output to the console - */ -async function monitorListingsUsingStreamAPI(client, timeInMs = 60000, pipeline = []) { - const collection = client.db('sample_airbnb').collection('listingsAndReviews'); - - // 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#stream for the stream() docs - changeStream.stream().pipe( - new stream.Writable({ - objectMode: true, - write: function (doc, _, cb) { - console.log(doc); - cb(); - } - }) - ); - - // Wait the given amount of time and then close the change stream - await closeChangeStream(timeInMs, changeStream); -} diff --git a/changeStreamsTestData.js b/changeStreamsTestData.js deleted file mode 100644 index 7867494..0000000 --- a/changeStreamsTestData.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * This script can be used to create, update, and delete sample data. - * This script is especially helpful when testing change streams. - */ -const { MongoClient } = require('mongodb'); - -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://:@/sample_airbnb?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 - * 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); - - try { - // Connect to the MongoDB cluster - await client.connect(); - - // Make the appropriate DB calls - const operaHouseViews = await createListing(client, { - name: "Opera House Views", - summary: "Beautiful apartment with views of the iconic Sydney Opera House", - property_type: "Apartment", - bedrooms: 1, - bathrooms: 1, - beds: 1, - address: { - market: "Sydney", - country: "Australia" - } - }); - - const privateRoomInLondon = await createListing(client, { - name: "Private room in London", - property_type: "Apartment", - bedrooms: 1, - bathroom: 1 - }); - - const beautifulBeachHouse = await createListing(client, { - name: "Beautiful Beach House", - summary: "Enjoy relaxed beach living in this house with a private beach", - bedrooms: 4, - bathrooms: 2.5, - beds: 7, - last_review: new Date() - }); - - await updateListing(client, operaHouseViews, { beds: 2 }); - - await updateListing(client, beautifulBeachHouse, { - address: { - market: "Sydney", - country: "Australia" - } - }); - - const italianVilla = await createListing(client, { - name: "Italian Villa", - property_type: "Entire home/apt", - bedrooms: 6, - bathrooms: 4, - address: { - market: "Cinque Terre", - country: "Italy" - } - }); - - const sydneyHarbourHome = await createListing(client, { - name: "Sydney Harbour Home", - bedrooms: 4, - bathrooms: 2.5, - address: { - market: "Sydney", - country: "Australia" - } - }); - - await deleteListing(client, sydneyHarbourHome); - - } finally { - // Close the connection to the MongoDB cluster - await client.close(); - } -} - -main().catch(console.error); - -/** - * Create a new Airbnb listing - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {Object} newListing The new listing to be added - * @returns {String} The id of the new listing - */ -async function createListing(client, newListing) { - // See http://bit.ly/Node_InsertOne for the insertOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").insertOne(newListing); - console.log(`New listing created with the following id: ${result.insertedId}`); - return result.insertedId; -} - -/** - * Update an Airbnb listing - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {String} listingId The id of the listing you want to update - * @param {object} updatedListing An object containing all of the properties to be updated for the given listing - */ -async function updateListing(client, listingId, updatedListing) { - // See http://bit.ly/Node_updateOne for the updateOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").updateOne({ _id: listingId }, { $set: updatedListing }); - - console.log(`${result.matchedCount} document(s) matched the query criteria.`); - console.log(`${result.modifiedCount} document(s) was/were updated.`); -} - -/** - * Delete an Airbnb listing - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {String} listingId The id of the listing you want to delete - */ -async function deleteListing(client, listingId) { - // See http://bit.ly/Node_deleteOne for the deleteOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").deleteOne({ _id: listingId }); - - console.log(`${result.deletedCount} document(s) was/were deleted.`); -} diff --git a/connection.js b/connection.js deleted file mode 100644 index 85a9e12..0000000 --- a/connection.js +++ /dev/null @@ -1,45 +0,0 @@ -const { MongoClient } = require('mongodb'); - -async function main() { - /** - * Connection URI. Update , , and to reflect your cluster. - * See https://docs.mongodb.com/ecosystem/drivers/node/ for more details - */ - const uri = "mongodb+srv://:@/sample_airbnb?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 - * 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); - - try { - // Connect to the MongoDB cluster - await client.connect(); - - // Make the appropriate DB calls - await listDatabases(client); - - } catch (e) { - console.error(e); - } finally { - // Close the connection to the MongoDB cluster - await client.close(); - } -} - -main().catch(console.error); - -/** - * Print the names of all available databases - * @param {MongoClient} client A MongoClient that is connected to a cluster - */ -async function listDatabases(client) { - databasesList = await client.db().admin().listDatabases(); - - console.log("Databases:"); - databasesList.databases.forEach(db => console.log(` - ${db.name}`)); -}; diff --git a/create.js b/create.js deleted file mode 100644 index d1d302c..0000000 --- a/create.js +++ /dev/null @@ -1,90 +0,0 @@ -const {MongoClient} = require('mongodb'); - -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://:@/sample_airbnb?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 - * 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); - - try { - // Connect to the MongoDB cluster - await client.connect(); - - // Make the appropriate DB calls - - // Create a single new listing - await createListing(client, - { - name: "Lovely Loft", - summary: "A charming loft in Paris", - bedrooms: 1, - bathrooms: 1 - } - ); - - // Create 3 new listings - await createMultipleListings(client, [ - { - name: "Infinite Views", - summary: "Modern home with infinite views from the infinity pool", - property_type: "House", - bedrooms: 5, - bathrooms: 4.5, - beds: 5 - }, - { - name: "Private room in London", - property_type: "Apartment", - bedrooms: 1, - bathroom: 1 - }, - { - name: "Beautiful Beach House", - summary: "Enjoy relaxed beach living in this house with a private beach", - bedrooms: 4, - bathrooms: 2.5, - beds: 7, - last_review: new Date() - } - ]); - } finally { - // Close the connection to the MongoDB cluster - await client.close(); - } -} - -main().catch(console.error); - -/** - * Create a new Airbnb listing - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {Object} newListing The new listing to be added - */ -async function createListing(client, newListing){ - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#insertOne for the insertOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").insertOne(newListing); - console.log(`New listing created with the following id: ${result.insertedId}`); -} - -/** - * Create multiple Airbnb listings - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {Object[]} newListings The new listings to be added - */ -async function createMultipleListings(client, newListings){ - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#insertMany for the insertMany() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").insertMany(newListings); - - console.log(`${result.insertedCount} new listing(s) created with the following id(s):`); - console.log(result.insertedIds); -} diff --git a/delete.js b/delete.js deleted file mode 100644 index 32b9646..0000000 --- a/delete.js +++ /dev/null @@ -1,96 +0,0 @@ -const { MongoClient } = require('mongodb'); - -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://:@/sample_airbnb?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 - * 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); - - try { - // Connect to the MongoDB cluster - await client.connect(); - - // Make the appropriate DB calls - - // DELETE ONE - // Check if a listing named "Cozy Cottage" exists. Run update.js if you do not have this listing. - await printIfListingExists(client, "Cozy Cottage"); - // Delete the "Cozy Cottage" listing - await deleteListingByName(client, "Cozy Cottage"); - // Check that the listing named "Cozy Cottage" no longer exists - await printIfListingExists(client, "Cozy Cottage"); - - // DELETE MANY - // Check if the listing named "Ribeira Charming Duplex" (last scraped February 16, 2019) exists - await printIfListingExists(client, "Ribeira Charming Duplex"); - // Check if the listing named "Horto flat with small garden" (last scraped February 11, 2019) exists - await printIfListingExists(client, "Horto flat with small garden"); - // Delete the listings that were scraped before February 15, 2019 - await deleteListingsScrapedBeforeDate(client, new Date("2019-02-15")); - // Check that the listing named "Ribeira Charming Duplex" still exists - await printIfListingExists(client, "Ribeira Charming Duplex"); - // Check that the listing named "Horto flat with small garden" no longer exists - await printIfListingExists(client, "Horto flat with small garden"); - - } finally { - // Close the connection to the MongoDB cluster - await client.close(); - } -} - -main().catch(console.error); - -/** - * Delete an Airbnb listing with the given name. - * Note: If more than one listing has the same name, only the first listing the database finds will be deleted. - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {string} nameOfListing The name of the listing you want to delete - */ -async function deleteListingByName(client, nameOfListing) { - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#deleteOne for the deleteOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").deleteOne({ name: nameOfListing }); - console.log(`${result.deletedCount} document(s) was/were deleted.`); -} - -/** - * Delete all listings that were last scraped prior to the given date - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {Date} date The date to check the last_scraped property against - */ -async function deleteListingsScrapedBeforeDate(client, date) { - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#deleteMany for the deleteMany() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").deleteMany({ "last_scraped": { $lt: date } }); - console.log(`${result.deletedCount} document(s) was/were deleted.`); -} - -/** - * Print information indicating if a listing with the given name exists. - * If a listing has the 'last_scraped' field, print that as well. - * Note: If more than one listing has the same name, only the first listing the database finds will be printed.. - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {String} nameOfListing The name of the listing you want to find - */ -async function printIfListingExists(client, nameOfListing) { - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#findOne for the findOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").findOne({ name: nameOfListing }); - - if (result) { - if (result.last_scraped) { - console.log(`Found a listing in the collection with the name '${nameOfListing}'. Listing was last scraped ${result.last_scraped}.`); - } else { - console.log(`Found a listing in the collection with the name '${nameOfListing}'`); - } - } else { - console.log(`No listings found with the name '${nameOfListing}'`); - } -} diff --git a/index.js b/index.js new file mode 100644 index 0000000..5fa53c6 --- /dev/null +++ b/index.js @@ -0,0 +1,55 @@ +const assert = require('assert'); +const { parseArgs } = require('node:util'); + +const { MongoClient, ServerApiVersion } = require("mongodb"); + +const options = { + uri: { + type: 'string', + }, + strict: { + type: 'boolean', + short: 's', + }, +}; +const { values } = parseArgs({ options, tokens: false }); + +const uri = values.uri; + +let client; + +if (values.strict) { + client = new MongoClient(uri, { + serverApi: { + version: ServerApiVersion.v1, + strict: true, + } + }); +} else { + client = new MongoClient(uri); +} + +async function run() { + try { + let res = await client.db('test').command({ ping: 1 }); + assert.equal(res.ok, 1, 'ping failed'); + res = await client.db('test').command({ dropDatabase: 1 }); + assert.equal(res.ok, 1, 'dropDatabase failed'); + + let docs = []; + for (let i = 1; i <= 4; i++) { + docs.push({ _id: i, a: i }); + } + + res = await client.db('test').collection('foo').insertMany(docs); + assert.equal(res.insertedCount, 4); + + const actual = await client.db('test').collection('foo').findOne({ a: 4 }); + assert.equal(actual.a, 4, 'Value should be 4'); + + } finally { + // Ensures that the client will close when you finish/error + await client.close(); + } +} +run().catch(console.dir); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0b03938 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,151 @@ +{ + "name": "nodejs-example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nodejs-example", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "mongodb": "6.5" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz", + "integrity": "sha512-XLNOMH66KhJzUJNwT/qlMnS4WsNDWD5ASdyaSH3EtK+F4r/CFGa3jT4GNi4mfOitGvWXtdLgQJkQjxSVrio+jA==", + "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==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", + "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/bson": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.6.0.tgz", + "integrity": "sha512-BVINv2SgcMjL4oYbBuCQTpE3/VKOSxrOA8Cj/wQP7izSzlBGVomdm+TcUd0Pzy0ytLSSDweCKQ6X3f5veM5LQA==", + "engines": { + "node": ">=16.20.1" + } + }, + "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==" + }, + "node_modules/mongodb": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.5.0.tgz", + "integrity": "sha512-Fozq68InT+JKABGLqctgtb8P56pRrJFkbhW0ux+x1mdHeyinor8oNzJqwLjV/t5X5nJGfTlluxfyMnOXNggIUA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.4.0", + "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", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.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.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", + "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^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==", + "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==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..200b07a --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "nodejs-example", + "version": "1.0.0", + "description": "This is a fork of https://github.com/FerretDB/nodejs-example, simplified to run with FerretDB.", + "main": "index.js", + "scripts": { + "test": "" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "mongodb": "6.5" + } +} diff --git a/read.js b/read.js deleted file mode 100644 index 91fc423..0000000 --- a/read.js +++ /dev/null @@ -1,107 +0,0 @@ -const { MongoClient } = require('mongodb'); - -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://:@/sample_airbnb?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 - * 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); - - try { - // Connect to the MongoDB cluster - await client.connect(); - - // Make the appropriate DB calls - - // Find the listing named "Infinite Views" that we created in create.js - await findOneListingByName(client, "Infinite Views"); - - // Find up to 5 listings with at least 4 bedrooms and at least 2 bathrooms - // If you recently ran create.js, a listing named Beautiful Beach House should be included in the results - await findListingsWithMinimumBedroomsBathroomsAndMostRecentReviews(client, { - minimumNumberOfBedrooms: 4, - minimumNumberOfBathrooms: 2, - maximumNumberOfResults: 5 - }); - - } finally { - // Close the connection to the MongoDB cluster - await client.close(); - } -} - -main().catch(console.error); - -/** - * Print an Airbnb listing with the given name - * Note: If more than one listing has the same name, only the first listing the database finds will be printed. - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {String} nameOfListing The name of the listing you want to find - */ -async function findOneListingByName(client, nameOfListing) { - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#findOne for the findOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").findOne({ name: nameOfListing }); - - if (result) { - console.log(`Found a listing in the collection with the name '${nameOfListing}':`); - console.log(result); - } else { - console.log(`No listings found with the name '${nameOfListing}'`); - } -} - -/** - * Print Airbnb listings with a minimum number of bedrooms and bathrooms. - * Results will be limited to the designated maximum number of results. - * Results will be sorted by the date of the last review in descending order. - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {object} queryParams The query params object - * @param {number} queryParams.minimumNumberOfBedrooms The minimum number of bedrooms - * @param {number} queryParams.minimumNumberOfBathrooms The minimum number of bathrooms - * @param {number} queryParams.maximumNumberOfResults The maximum number of Airbnb listings to be printed - */ -async function findListingsWithMinimumBedroomsBathroomsAndMostRecentReviews(client, { - minimumNumberOfBedrooms = 0, - minimumNumberOfBathrooms = 0, - maximumNumberOfResults = Number.MAX_SAFE_INTEGER -} = {}) { - - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#find for the find() docs - const cursor = client.db("sample_airbnb").collection("listingsAndReviews") - .find({ - bedrooms: { $gte: minimumNumberOfBedrooms }, - bathrooms: { $gte: minimumNumberOfBathrooms } - } - ) - .sort({ last_review: -1 }) - .limit(maximumNumberOfResults); - - // Store the results in an array - const results = await cursor.toArray(); - - // Print the results - if (results.length > 0) { - console.log(`Found listing(s) with at least ${minimumNumberOfBedrooms} bedrooms and ${minimumNumberOfBathrooms} bathrooms:`); - results.forEach((result, i) => { - 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: ${date}`); - }); - } else { - console.log(`No listings found with at least ${minimumNumberOfBedrooms} bedrooms and ${minimumNumberOfBathrooms} bathrooms`); - } -} diff --git a/template.js b/template.js deleted file mode 100644 index 2785d10..0000000 --- a/template.js +++ /dev/null @@ -1,33 +0,0 @@ -const { MongoClient } = require('mongodb'); - -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://:@/sample_airbnb?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 - * 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); - - try { - // Connect to the MongoDB cluster - await client.connect(); - - // Make the appropriate DB calls - - } finally { - // Close the connection to the MongoDB cluster - await client.close(); - } -} - -main().catch(console.error); - -// Add functions that make DB calls here diff --git a/transaction-bankingexample.js b/transaction-bankingexample.js deleted file mode 100644 index 9ed5479..0000000 --- a/transaction-bankingexample.js +++ /dev/null @@ -1,128 +0,0 @@ -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 deleted file mode 100644 index 42505b1..0000000 --- a/transaction-inventoryexample.js +++ /dev/null @@ -1,127 +0,0 @@ -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 deleted file mode 100644 index 33ff107..0000000 --- a/transaction.js +++ /dev/null @@ -1,152 +0,0 @@ -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 -// Be sure you have run usersCollection.js prior to running this script - -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://:@/sample_airbnb?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 - * 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); - - 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 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 - - // 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); - - if (transactionResults) { - console.log("The reservation was successfully created."); - } else { - console.log("The transaction was intentionally aborted."); - } - } catch (e) { - console.log("The transaction was aborted due to an unexpected error: " + e); - } 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; -} diff --git a/update.js b/update.js deleted file mode 100644 index e82e894..0000000 --- a/update.js +++ /dev/null @@ -1,124 +0,0 @@ -const { MongoClient } = require('mongodb'); - -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://:@/sample_airbnb?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 - * 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); - - try { - // Connect to the MongoDB cluster - await client.connect(); - - // Make the appropriate DB calls - - // UPDATE - // Print the Infinite Views listing - await findListingByName(client, "Infinite Views"); - // Update the Infinite Views listing to have 6 bedrooms and 8 beds - await updateListingByName(client, "Infinite Views", { bedrooms: 6, beds: 8 }); - // Print the updated Infinite Views listing - await findListingByName(client, "Infinite Views"); - - // UPSERT - // Check if a listing named Cozy Cottage is in the db - await findListingByName(client, "Cozy Cottage"); - // Upsert the Cozy Cottage listing - await upsertListingByName(client, "Cozy Cottage", { name: "Cozy Cottage", bedrooms: 2, bathrooms: 1 }); - // Print the details of the Cozy Cottage listing - await findListingByName(client, "Cozy Cottage"); - // Upsert the Cozy Cottage listing - await upsertListingByName(client, "Cozy Cottage", { beds: 2 }); - // Print the details of the Cozy Cottage listing - await findListingByName(client, "Cozy Cottage"); - - // UPDATE MANY - // Update all listings so they have a property type - await updateAllListingsToHavePropertyType(client); - // Print the details of the Cozy Cottage listing that should now have a property type - await findListingByName(client, "Cozy Cottage"); - - } finally { - // Close the connection to the MongoDB cluster - await client.close(); - } -} - -main().catch(console.error); - -/** - * Update an Airbnb listing with the given name - * Note: If more than one listing has the same name, only the first listing the database finds will be updated. - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {string} nameOfListing The name of the listing you want to update - * @param {object} updatedListing An object containing all of the properties to be updated for the given listing - */ -async function updateListingByName(client, nameOfListing, updatedListing) { - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#updateOne for the updateOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").updateOne({ name: nameOfListing }, { $set: updatedListing }); - - console.log(`${result.matchedCount} document(s) matched the query criteria.`); - console.log(`${result.modifiedCount} document(s) was/were updated.`); -} - -/** - * Upsert an Airbnb listing with the given name. - * If a listing with the given name exists, it will be updated. - * If a listing with the given name does not exist, it will be inserted. - * Note: If more than one listing has the same name, only the first listing the database finds will be updated. - * Note: For educational purposes, we have split the update and upsert functionality into separate functions. - * Another option is to have a single function where a boolean param indicates if the update should be an upsert. - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {string} nameOfListing The name of the listing you want to upsert - * @param {object} updatedListing An object containing all of the properties to be upserted for the given listing - */ -async function upsertListingByName(client, nameOfListing, updatedListing) { - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#updateOne for the updateOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").updateOne({ name: nameOfListing }, { $set: updatedListing }, { upsert: true }); - console.log(`${result.matchedCount} document(s) matched the query criteria.`); - - if (result.upsertedCount > 0) { - console.log(`One document was inserted with the id ${result.upsertedId._id}`); - } else { - console.log(`${result.modifiedCount} document(s) was/were updated.`); - } -} - -/** - * Update all Airbnb listings that do not have a property type so they have property_type 'Unknown' - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - */ -async function updateAllListingsToHavePropertyType(client) { - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#updateMany for the updateMany() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").updateMany({ property_type: { $exists: false } }, { $set: { property_type: "Unknown" } }); - console.log(`${result.matchedCount} document(s) matched the query criteria.`); - console.log(`${result.modifiedCount} document(s) was/were updated.`); -} - -/** - * Print an Airbnb listing with the given name - * Note: If more than one listing has the same name, only the first listing the database finds will be printed. - * @param {MongoClient} client A MongoClient that is connected to a cluster with the sample_airbnb database - * @param {String} nameOfListing The name of the listing you want to find - */ -async function findListingByName(client, nameOfListing) { - // See https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#findOne for the findOne() docs - const result = await client.db("sample_airbnb").collection("listingsAndReviews").findOne({ name: nameOfListing }); - - if (result) { - console.log(`Found a listing in the db with the name '${nameOfListing}':`); - console.log(result); - } else { - console.log(`No listings found with the name '${nameOfListing}'`); - } -} diff --git a/usersCollection.js b/usersCollection.js deleted file mode 100644 index ba492eb..0000000 --- a/usersCollection.js +++ /dev/null @@ -1,76 +0,0 @@ -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 , , and to reflect your cluster. - * See https://docs.mongodb.com/drivers/node/ for more details - */ - const uri = "mongodb+srv://:@/sample_airbnb?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 - * 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); - - 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 https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#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); -}