MongoDB JavaScript (NodeJS) Connector Simplified 101

on Data Integration, JavaScript, Mongo Shell, MongoDB • February 28th, 2022 • Write for Hevo

MongoDB Javascript FI

MongoDB is a NoSQL (non-relational) database. It is scalable and may be used to hold a variety of data kinds. MongoDB, unlike tables, organizes data into documents and collections, allowing users to store even unstructured data. You’ll need to simplify your development and management activities when using MongoDB. 

Websites and Applications are used by businesses to run their online operations. Databases are used to store, retrieve, and satisfy all data requirements on these websites. Almost all modern websites are interactive and dynamic, responding to user inputs and data. JavaScript is a computer language that is commonly used in conjunction with HTML and CSS to give websites dynamic functionality. It is used to programmatically manipulate the content of web pages.

MongoDB JavaScript (NodeJS) connection will make the websites and applications user interactive, allow companies to use data through MongoDB JavaScript (NodeJS) connection, and manipulate data. In this article, you will have a brief introduction to MongoDB Database, JavaScript, and the steps to set up MongoDB JavaScript (NodeJS) connection.

Table of Contents

What is MongoDB?

MongoDB JavaScript: mongodb logo
Image Source

MongoDB is a well-known Open-Source NoSQL database built on the C++ programming language. MongoDB is a document-oriented database that uses JSON-like documents and a Dynamic Schema to store information. It means that while saving your data, you won’t have to worry about the Data Structure, the number of fields, or the types of fields used to store values. JSON objects are identical to MongoDB Documents.

You can change the structure of records by simply adding new fields or deleting existing ones (which MongoDB refers to as Documents). This functionality can be used to describe Hierarchical Relationships, Store Arrays, and other more sophisticated Data Structures in MongoDB. MongoDB is being utilized to store enormous volumes of data by a number of digital companies, including Facebook, eBay, Adobe, and Google.

Key Features of MongoDB

MongoDB has a variety of unique features that make it a better alternative than other standard databases. Some of these characteristics are as follows:

  • Horizontal Scalability: MongoDB’s sharding allows for this. Sharding is the process of sharing data over multiple servers. The Shard Key is used to partition a large amount of data into data chunks, which are then evenly distributed among Shards across several Physical Servers.
  • Schema-Less Database: A database with no schemas maintains a variety of documents in a single collection (the equivalent of a table). To put it another way, a single MongoDB collection can include several documents, each with its own set of Fields, Content, and Size. Unlike Relational Databases, there is no requirement that one document is equivalent to another. Because of this functionality, MongoDB gives users a lot of flexibility.
  • Index-based Document: Every field in a MongoDB database is indexed with Primary and Secondary Indices, making data retrieval easier.
  • Replication: MongoDB enables high data availability by making multiple copies of the data and transmitting them to a separate server, allowing the data to be recovered even if one server fails.

What is JavaScript?

MongoDB JavaScript: javascript logo
Image Source

JavaScript is a simple programming language that enables users to add advanced features and functionality to websites. It provides behavior to a webpage, and javascript is used by roughly 97 percent of all websites on the internet. On the client-side, websites use JavaScript and frequently use third-party libraries. For running JavaScript code on clients’ devices, all major web browsers include a JavaScript engine. The static web page is transformed into an interactive web page using JavaScript.

JavaScript is a scripting language for building networked applications. You can write JavaScript in any code editor or file editor, save the file with the.js extension, and run it in any major web browser without having to install any other packages or libraries.

Key Features of JavaScript

The following are some of JavaScript’s features:

  • Platform Independent: Since web browsers interpret JavaScript, it eliminates any platform reliance, compatibility, and other issues. JavaScript adds functionality to a variety of browsers that help reduce server load and network traffic.
  • Dynamic Typing: It implies that the types of variables are determined by the value that is stored.
  • Async Processing: JavaScript supports async programming, which means it may perform asynchronous tasks without waiting for a response, which can slow down request processing.
  • Functional Approach: JavaScript has a functional approach to object creation, with each function Object() { [native code] } function representing a different type of object.

Simplify MongoDB ETL Using Hevo’s No-code Data Pipeline

Hevo Data is a No-code Data Pipeline that offers a fully managed solution to set up Data Integration for 100+ Data Sources (including 40+ Free sources) and will let you directly load data from sources like MongoDB to a Data Warehouse or the Destination of your choice. It will automate your data flow in minutes without writing any line of code. Its fault-tolerant architecture makes sure that your data is secure and consistent. Hevo provides you with a truly efficient and fully automated solution to manage data in real-time and always have analysis-ready data. 

Get Started with Hevo for Free

Let’s look at some of the salient features of Hevo:

  • Fully Managed: It requires no management and maintenance as Hevo is a fully automated platform.
  • Data Transformation: It provides a simple interface to perfect, modify, and enrich the data you want to transfer. 
  • Real-Time: Hevo offers real-time data migration. So, your data is always ready for analysis.
  • Schema Management: Hevo can automatically detect the schema of the incoming data and map it to the destination schema.
  • Connectors: Hevo supports 100+ Integrations to SaaS platforms FTP/SFTP, Files, Databases, BI tools, and Native REST API & Webhooks Connectors. It supports various destinations including Google BigQuery, Amazon Redshift, Snowflake, Firebolt, Data Warehouses; Amazon S3 Data Lakes; Databricks; and MySQL, SQL Server, TokuDB, MongoDB, PostgreSQL Databases to name a few.  
  • Secure: Hevo has a fault-tolerant architecture that ensures that the data is handled in a secure, consistent manner with zero data loss.
  • Hevo Is Built To Scale: As the number of sources and the volume of your data grows, Hevo scales horizontally, handling millions of records per minute with very little latency.
  • Live Monitoring: Advanced monitoring gives you a one-stop view to watch all the activities that occur within Data Pipelines.
  • Live Support: Hevo team is available round the clock to extend exceptional support to its customers through chat, email, and support calls.
Sign up here for a 14-Day Free Trial!

MongoDB JavaScript (NodeJS) Configuration Steps

Step 1: Install NodeJS and MongoDB JavaScript (NodeJS) Driver

First, make sure you have a version of Node.js that is supported. Node 4.x or higher is required for the current version of MongoDB JavaScript (NodeJS) Driver. Node.js 14.15.4 is being used for these examples. For additional information on which version of Node.js is required for each version of the MongoDB JavaScript (NodeJS) driver, see the MongoDB Compatibility documentation.

The MongoDB JavaScript (NodeJS) Driver makes it simple to work with MongoDB databases from Node.js applications. To connect to your database and run the queries discussed in this Quick Start series, you’ll need the MongoDB JavaScript (NodeJS) driver. If you don’t already have the MongoDB JavaScript (NodeJS) Driver installed, use the following command to do so.

npm install mongodb

This MongoDB JavaScript (NodeJS) driver was installed with version 3.6.4 at the time of writing. The current MongoDB JavaScript (NodeJS) driver version number may be found by running npm list mongodb. See the official documentation for further information on the MongoDB JavaScript (NodeJS) driver and installation.

Step 2: Create a Free Cluster and get your Cluster’s Connection Information

  • After that, you’ll require a MongoDB database. Atlas, MongoDB’s fully-managed database-as-a-service, is the simplest way to get started with MongoDB.
  • To get started, go to Atlas and establish a new cluster using the free tier. A cluster is a collection of computers where copies of your database are stored at a high level. Load the example data into your tier once it’s been built. 
  • The next step is to get your cluster ready to connect. Navigate to your cluster in Atlas and click CONNECT. The Cluster Connection Wizard will show on the screen.
  • If you haven’t already done so, the wizard will prompt you to add your current IP address to the IP Access List and create a MongoDB User. Make a note of the new MongoDB User’s Username and Password because you’ll need them later.
  • Wizard will then ask you to select a connection method. Connect Your Application is the option to choose. Select Node.js and 3.6 or later when the Wizard asks you to choose your MongoDB JavaScript (NodeJS) driver version. Copy the connection string that has been provided.

See the official documentation for further information on how to use the Connection Wizard and follow the procedures outlined above.

Step 3: Import MongoClient

MongoClient is an export from the MongoDB module that you’ll use to connect to a MongoDB Database. You can connect to a cluster, access the database in that cluster, and end the connection to that cluster using a MongoClient instance.

const {MongoClient} = require('mongodb');

Step 4: Create Your Primary Function

Let’s make a main() asynchronous function that connects to our MongoDB Cluster, calls methods that query our database and then disconnects from your Cluster.

async function main() {
	// we'll add code here soon
}

Inside main(), the first thing we need to do is construct a constant for our connection URI. The connection URI is the connection string you copied in the previous section and pasted into Atlas. Remember to adjust <username> and <password> to the credentials for the user you created in the previous section when you paste the connection string. A <dbname> placeholder is included in the connection string. You’ll be using the sample_airbnb database in these examples, so replace <dbname> with sample_airbnb.

/**
 * Connection URI. Update <username>, <password>, and <your-cluster-url> to reflect your cluster.
 * See https://docs.mongodb.com/ecosystem/drivers/node/ for more details
 */
const uri = "mongodb+srv://<username>:<password>@<your-cluster-url>/test?retryWrites=true&w=majority";

You may build a MongoClient instance now that you know the URL.

const client = new MongoClient(uri);

Now you’re ready to connect to our Cluster with MongoClient. A promise will be returned by client.connect(). When you use client.connect(), you’ll use the await keyword to signify that you shouldn’t do anything else until that operation is finished.

await client.connect();

You’re now ready to work with your database. Let’s create a function that reports the database names in this Cluster. To increase the readability of your software, it’s often a good idea to put this logic in well-named functions. As you learn how to construct different types of queries, you’ll create new functions identical to the one you’re creating here throughout these steps. Let’s call a method listDatabases() for now.

await listDatabases(client);

To handle any unexpected failures, wrap your calls to functions that interface with the database in a try/catch statement.

try {
    await client.connect();

    await listDatabases(client);
 
} catch (e) {
    console.error(e);
}

You’ll finish the try/catch with a final statement to ensure that you close the connection to the Cluster.

finally {
    await client.close();
}

You must call our main() function once it has been written. Let’s use the console to send the errors.

main().catch(console.error);

When it’s all put together, the main() function and the call to it should look like this.

async function main(){
    /**
     * Connection URI. Update <username>, <password>, and <your-cluster-url> to reflect your cluster.
     * See https://docs.mongodb.com/ecosystem/drivers/node/ for more details
     */
    const uri = "mongodb+srv://<username>:<password>@<your-cluster-url>/test?retryWrites=true&w=majority";

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 {
        await client.close();
    }
}

main().catch(console.error);

Step 5: List the Databases in Your Cluster

This function will get a list of your Cluster’s databases and output the results to the console.

async function listDatabases(client){
    databasesList = await client.db().admin().listDatabases();
 
    console.log("Databases:");
    databasesList.databases.forEach(db => console.log(` - ${db.name}`));
};

You’ve been putting a lot of code in place. Save your changes and rename the file connection.js to reflect your changes. Visit the nodejs-quickstart GitHub repo to see a copy of the entire file.

Step 6: Implement Your NodeJS Script

Now it’s time to put your code to the test! Run the following command in your terminal to execute your script: 

node connection.js 

What are some other ways to Connect to MongoDB?

Connect to a Replica Set

A MongoDB replica set deployment is a collection of linked instances that all hold the same data. Data Redundancy and excellent data availability are provided by this combination of instances.

To connect to a replica set deployment, use the hostname and port numbers of each instance separated by a comma in the connection string, as well as the replica set name as the value of the replicaSet option.

mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=myRs

By default, the MongoDB JavaScript (NodeJS) driver performs the following tasks while establishing a connection:

  • When a member’s address is given, it finds all other members in the replica set.
  • Dispatches operations to the appropriate member, such as writing against the primary.

Connect to a MongoDB Server on your Local Instance

You must take the following steps if you need to run a MongoDB Server on your local PC for development reasons rather than using an Atlas Cluster:

  • MongoDB Server is available in two versions: Community and Enterprise.
  • MongoDB Server must be installed and configured.
  • Launch the Server.

Specify your connection string in your MongoDB JavaScript (NodeJS) driver connection code after you’ve successfully started your MongoDB Server.

If your MongoDB Server is locally installed, you can use the connection string “mongodb://localhost:<port>” where <port> is the port number on which your server is set to accept incoming connections. You can refer to Manual on Connection Strings if you need to supply a different hostname or IP address. Replace the connection string in the Connect to MongoDB Atlas code sample with your server’s connection string to see whether you can connect.

How to use MongoDB Transactions in NodeJS?

Most use cases do not necessitate the use of multi-document MongoDB Javascript transactions, as you may have discovered while working with MongoDB. You’ll rarely need to execute a multi-document transaction if you model your data using our rule of thumb Data that is accessed together should be kept together. Indeed, it was a hard time coming up with a use case for the Airbnb dataset that would necessitate a multi-document transaction.

Here is a plausible example. Let’s assume the people should be able to make reservations in the sample_airbnb database.

Step 1: Creating a Transaction in Node.js

Getting Copy of Node.js Template

Here is a basic template for a Node.js script that accesses an Atlas cluster to make following along with this blog post easier.

  • Template.js is available for download.
  • In your preferred Code Editor, open template.js.
  • To point to your Atlas cluster, change the Connection URI. Refer back to the first post in this series if you’re not sure how to do that.
  • Save the file with the extension transaction.js.

You can run this file in your shell by typing node transaction.js. No output is expected at this point because the file just opens and closes a connection to your Atlas cluster. If you encounter DeprecationWarnings, disregard them for the time being.

Creating Helper Function

Let’s start by building a Helper Function. This function creates a reservation document, which you’ll utilize later.

In transaction.js, paste the following function:

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;
  }

Here is an example of what this function does to give you a better picture of what it does. This function could be called from within the main() function:

createReservationDocument("Infinite Views",
    [new Date("2019-12-31"), new Date("2020-01-01")],
    { pricePerNight: 180, specialRequests: "Late checkout", breakfastIncluded: true });

The following is what the function would return:

{ 
   name: 'Infinite Views',
   dates: [ 2019-12-31T00:00:00.000Z, 2020-01-01T00:00:00.000Z ],
   pricePerNight: 180,
   specialRequests: 'Late checkout',
   breakfastIncluded: true 
}

Creating Function for Transaction

Let’s make a function that will make the reservation in the database.

Create an asynchronous method named createReservation while still working in transaction.js. The function should take a MongoClient as a parameter, as well as the user’s email address, the name of the Airbnb listing, the reservation dates, and any other reservation details.

async function createReservation(client, userEmail, nameOfListing, reservationDates, reservationDetails) {
}

You must now have access to the collections that will be updated in this function. To createReservation(), add the following code.

const usersCollection = client.db("sample_airbnb").collection("users");
const listingsAndReviewsCollection = client.db("sample_airbnb").collection("listingsAndReviews");

Let’s start by invoking the helper function you developed in the previous section to construct the reservation document. In createReservation(), paste the following code.

const reservation = createReservationDocument(nameOfListing, reservationDates, reservationDetails);

A session must be associated with each transaction and related processes. Begin a session behind the existing code in createReservation().

const session = client.startSession();

You have the ability to define transaction choices. More information on these options can be found in the MongoDB JavaScript (NodeJS) driver documentation. Replace the old code in createReservation() with the following code.

const transactionOptions = {
    readPreference: 'primary',
    readConcern: { level: 'local' },
    writeConcern: { w: 'majority' }
};

Create a try block beneath the existing code in createReservation(), followed by a catch block, and lastly a finally block.

try {

} catch(e){

} finally {

}

To start a transaction, perform a callback function, and commit (or abort on fail), you can utilize ClientSession’s withTransaction() method. You must give a method to withTransaction() that will run inside the transaction. Within try{}, add a call to withTransaction(). Let’s start by giving withTransaction() an anonymous asynchronous function.

const transactionResults = await session.withTransaction(async () => {}, transactionOptions);

You’re giving an anonymous callback function to withTransaction() that does nothing right now. Let’s start building the database procedures you wish to invoke from within that function one by one. To get started, you’ll add a reservation to the reservations array in the relevant user document. Inside the anonymous function that is being supplied to withTransaction(), paste the following.

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.`);

You should check if the reservation date is already stated in the listing’s datesReserved array to ensure that an Airbnb listing is not double-booked for any given date. If this is the case, you should cancel the transaction. The update to the user document you made in the previous step will be rolled back if the transaction is aborted. Replace the present code in the anonymous function with the following.

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;
}

The last thing you need to do in the transaction is to add the reservation dates to the listingsAndReviews collection’s datesReserved array. Replace the present code in the anonymous function with the following.

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.`);

If the deal goes through, you’ll want to know. You will know the transaction was successful if transactionResults is defined. If transactionResults is undefined, you will know the code was aborted purposely. Paste the following code beneath the transactionResults constant’s definition.

if (transactionResults) {
    console.log("The reservation was successfully created.");
} else {
    console.log("The transaction was intentionally aborted.");
}

Let’s keep track of any errors that occur. Inside catch(e), paste the following:

console.log("The transaction was aborted due to an unexpected error: " + e);

Whatever occurs, you must call a halt to this meeting. Paste the following inside finally{}:

await session.endSession();

The following is what your function should look like in Mongodb Javascript at this point:

sync function createReservation(client, userEmail, nameOfListing, reservationDates, reservationDetails) {

    const usersCollection = client.db("sample_airbnb").collection("users");
    const listingsAndReviewsCollection = client.db("sample_airbnb").collection("listingsAndReviews");

    const reservation = createReservationDocument(nameOfListing, reservationDates, reservationDetails);

    const session = client.startSession();

    const transactionOptions = {
        readPreference: 'primary',
        readConcern: { level: 'local' },
        writeConcern: { w: 'majority' }
    };

    try {
        const transactionResults = await session.withTransaction(async () => {

            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.`);


            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;
            }

            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 {
        await session.endSession();
    }

}

Step 2: Creating a Transaction in Node.js

Let’s put our new function to the test now that you’ve built one that creates a reservation using a transaction. Let’s make a reservation for Leslie for the nights of December 31, 2019, and January 1, 2020, at the “Infinite Views” listing.

Call your createReservation() function within main(), beneath the line that reads Make the proper 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 });

Make a backup of your Mongodb Javascript file. In your shell, run node transaction.js to run your script.

In your shell, the following output will appear.

1 document(s) found in the users collection with the email address leslie@example.com.
1 document(s) was/were updated to include the reservation.
1 document(s) found in the listingsAndReviews collection with the name Infinite Views.
1 document(s) was/were updated to include the reservation dates.
The reservation was successfully created.

The reservation is now in Leslie’s document in the users collection.

{
"_id": {"$oid":"5dd68bd03712fe11bebfab0c"},
"email": "leslie@example.com",
"name": "Leslie Yepp",
"reservations": [
    {
    "name": "Infinite Views", 
    "dates": [
        {"$date": {"$numberLong":"1577750400000"}},
        {"$date": {"$numberLong":"1577836800000"}}
        ],
    "pricePerNight": {"$numberInt":"180"},
    "specialRequests": "Late checkout",
    "breakfastIncluded": true
    }
]
}

The reservation dates are now included in the listingsAndReviews collection’s “Infinite Views” listing.

{
"_id": {"$oid": "5dbc20f942073d6d4dabd730"},
"name": "Infinite Views",
"summary": "Modern home with infinite views from the infinity pool",
"property_type": "House",
"bedrooms": {"$numberInt":"6"},
"bathrooms": {"$numberDouble":"4.5"},
"beds": {"$numberInt":"8"},
"datesReserved": [
    {"$date": {"$numberLong": "1577750400000"}},
    {"$date": {"$numberLong": "1577836800000"}}
]
}

Understanding MongoDB JavaScript (NodeJS) CRUD Operations

1) Create

Create One Document 

Let’s start by making a brand-new Airbnb listing. You can achieve this by using Collection’s insertOne() command. Inserts a single document into the collection with insertOne(). The new document (of type object) will be inserted in the solely needed parameter. If your new document does not have a _id column, the MongoDB JavaScript (NodeJS) driver will construct one for us automatically.

The following is how the function for creating a new listing will work:

async function createListing(client, newListing){
    const result = await client.db("sample_airbnb").collection("listingsAndReviews").insertOne(newListing);
    console.log(`New listing created with the following id: ${result.insertedId}`);
}

This function can be called by passing a connected MongoClient and an object containing listing information.

await createListing(client,
        {
            name: "Lovely Loft",
            summary: "A charming loft in Paris",
            bedrooms: 1,
            bathrooms: 1
        }
    );

The following is an example of the output:

New listing created with the following id: 5d9ddadee415264e135ccec8

Since you didn’t include a field named _id in the document, the MongoDB JavaScript (NodeJS) driver built one automatically. The document you create will have a different _id from the one indicated above. 

Create Multiple Documents 

You may want to insert multiple documents at once on occasion. You have the option of continually calling insertOne(). The problem is that, depending on how your code is designed, you may end up having to wait for each insert operation to complete before moving on to the next, resulting in slow code.

Instead, you can use Collection’s insertMany() method. insertMany() will populate your collection with an array of documents.

One thing to keep in mind with insertMany() is that it can be ordered. If order is true, the documents will be placed in the array in the order specified. The remaining documents will not be inserted if any of the inserts fail (for example, if you try to insert a document with an _id that is already in use by another document in the collection). If an order is false, the documents will not be placed in the array’s specified order. Regardless of whether any of the other inserts fail, MongoDB will attempt to insert all of the documents in the specified array. Ordered is set to true by default.

Let’s construct a function that will generate several Airbnb listings.

async function createMultipleListings(client, newListings){
    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);       
}

This function can be called by passing a connected MongoClient and an array of objects containing listing information.

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()
    }
]);

The following is an example of the output of calling createMultipleListings():

3 new listing(s) created with the following id(s):
{ 
  '0': 5d9ddadee415264e135ccec9,
  '1': 5d9ddadee415264e135cceca,
  '2': 5d9ddadee415264e135ccecb 
}

When you used insertOne(), the MongoDB JavaScript (NodeJS) Driver automatically built the _id field for you. When you called insertMany(), the MongoDB JavaScript (NodeJS) Driver created the _id field for you again.

2) Update 

Update Single Document 

Upsert is one of the parameters you can supply to updateOne(). Upsert is a useful tool that allows you to update or insert a document if one already exists.

Let’s imagine you wanted to make sure an Airbnb listing with a specific name had a specific amount of bedrooms and bathrooms. You’d have to use findOne() first to see if the document existed if you didn’t use upsert. If the document already existed, you’d update it with updateOne(). You’d use insertOne() to create the document if it didn’t already exist. You can bundle all of those capability into an one command using upsert.

With one crucial modification, the function to upsert a listing with a certain name can be nearly identical to the function defined above: In the options argument for updateOne(), you’ll pass {upsert: true}.

async function upsertListingByName(client, nameOfListing, updatedListing) {
    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.`);
    }
}

Let’s imagine you’re not sure if a listing called “Cozy Cottage” exists in the database or, if it does if it contains outdated information. In any case, you want to make sure that the information in the collection is up to current. With a connected MongoClient, the name of the listing, and an object containing the up-to-date data that should be in the listing, you may run upsertListingByName().

await upsertListingByName(client, "Cozy Cottage", { name: "Cozy Cottage", bedrooms: 2, bathrooms: 1 });

If the document had never been before, the function’s output would look something like this:

0 document(s) matched the query criteria.
One document was inserted with the id 5db9d9286c503eb624d036a1

In the listingsAndReviews collection, you’ve included a new document:

{ 
    _id: 5db9d9286c503eb624d036a1,
    name: 'Cozy Cottage',
    bathrooms: 1,
    bedrooms: 2 
}

You can use upsertListingByName() again if we find out more about the “Cozy Cottage” listing.

await upsertListingByName(client, "Cozy Cottage", { beds: 2 });

And you’d get the following result.

1 document(s) matched the query criteria.
1 document(s) was/were updated.

A new field called “beds” has been added to the paper.

{ 
    _id: 5db9d9286c503eb624d036a1,
    name: 'Cozy Cottage',
    bathrooms: 1,
    bedrooms: 2,
    beds: 2 
}

Update Multiple Documents 

You might want to change multiple documents at the same time. In this instance, you can use the updateMany() method of Collection. updateMany(), like updateOne(), expects a filter of type object and an update of type object. You can also choose to include type object options.

3) Delete

Delete Single Document 

To get started, delete a single Airbnb listing from the listingsAndReviews collection.

You can delete a single document by using the deleteOne() method in Collection. Only one parameter is required for deleteOne(): an object filter. The filter is used to find the document that needs to be deleted. The filter is similar to the query parameter you used in findOne() and the filter parameter you used in updateOne(). You can search for all documents in the collection by including zero properties in the filter, or you can refine your search by including one or more properties.

Optionally, deleteOne() has an options parameter. For additional information on these choices, see the deleteOne() documentation.

deleteOne() removes the first document that matches the specified query. Only one document will be destroyed if more than one document matches the query. The first document found in natural order will be erased if you don’t provide a filter.

Assume you wish to remove an Airbnb listing with a specific name. To do this, you can utilise deleteOne(). The name of the listing will be included in the filter parameter. You can write a function to remove a listing with a specific name from the database.

async function deleteListingByName(client, nameOfListing) {
    const result = await client.db("sample_airbnb").collection("listingsAndReviews")
            .deleteOne({ name: nameOfListing });
    console.log(`${result.deletedCount} document(s) was/were deleted.`);
}

Let’s imagine you wish to remove the “Cozy Cottage” Airbnb listing you made earlier in this section. By giving a connected MongoClient and the name “Cozy Cottage,” you can call deleteListingsByName().

await deleteListingByName(client, "Cozy Cottage");

The output of running the command above is as follows.

1 document(s) was/were deleted.

Delete Multiple Documents 

You might want to remove multiple documents at the same time. In this scenario, you can utilize the deleteMany() method of Collection. deleteMany(), like deleteOne(), requires an object filter to be passed. You can also choose to include type object options.

Assume you want to get rid of documents that haven’t been updated in a while. With a filter that searches for documents scraped before a certain date, you may run deleteMany(). The following is an example of the function.

async function deleteListingsScrapedBeforeDate(client, date) {
    const result = await client.db("sample_airbnb").collection("listingsAndReviews")
        .deleteMany({ "last_scraped": { $lt: date } });
    console.log(`${result.deletedCount} document(s) was/were deleted.`);
}

With a connected MongoClient and a Date instance that represents February 15, you can execute deleteListingsScrapedBeforeDate() to delete listings scraped prior to February 15, 2019.

await deleteListingsScrapedBeforeDate(client, new Date("2019-02-15"));

When you run the command above, you’ll get the following output.

606 document(s) was/were deleted.

Our collection now only contains documents that have been scrapped recently.

4) Read

Read Single Document 

Let’s start by searching the listingsAndReviews collection for an Airbnb listing by name.

You can use Collection’s findOne() to look for a document. The first document that matches the supplied query will be returned by findOne(). Only one document will be returned, even if more than one document fits the query.

There is only one needed parameter for findOne(): an object query. MongoDB will use zero or more properties in the query object to find a document in the collection. You can just pass an empty object to query all documents in a collection without restricting your results in any manner.

You’ll include the name field in the query object we send to findOne() since you want to find an Airbnb listing with a certain name:

findOne({ name: nameOfListing })

The following is an example of a function to find a listing by querying the name field:

async function findOneListingByName(client, nameOfListing) {
    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}'`);
    }
}

This function can be called by giving a connected MongoClient and the name of the listing you want to find. Let’s look for the “Infinite Views” listing you made earlier in this part.

await findOneListingByName(client, "Infinite Views");

The final product should look something like this.

Found a listing in the collection with the name 'Infinite Views':
{ 
  _id: 5da9b5983e104518671ae128,
  name: 'Infinite Views',
  summary: 'Modern home with infinite views from the infinity pool',
  property_type: 'House',
  bedrooms: 5,
  bathrooms: 4.5,
  beds: 5 
}

It’s important to note that the _id of the document in your database will differ from the _id in the example output above.

Read Multiple Documents 

Let’s look at how to query for numerous documents at once now that you know how to query for a single item. You can accomplish so by dialing Collection’s locate number using find().

The query object is the initial parameter for find(), just like it is for findOne(). In the query object, you can include zero to many properties.

Let’s say you want to find all Airbnb listings with at least two bedrooms and two bathrooms. You could accomplish that by dialing the following number:

client.db("sample_airbnb").collection("listingsAndReviews").find(
        {
            bedrooms: { $gte: minimumNumberOfBedrooms },
            bathrooms: { $gte: minimumNumberOfBathrooms }
        }
    );

As you can see, your query object has two properties: one for bedrooms and one for bathrooms. The $gte comparison query operator can be used to find documents with bedrooms greater than or equal to a certain number. You can do the same to meet your demand for a certain number of restrooms. Other comparison query operators are available in MongoDB and can be used in your queries. 

A Cursor will be returned by the query above. A Cursor is a tool for traversing a query’s result set.

The cursor’s functions can also be used to change which documents are included in the results. Let’s imagine you want to order our results so that the ones with the most recent reviews appear first. The last review field could be used to sort the results using Cursor’s sort() function. You may arrange the results in descending order (by supplying -1 to sort()) to return the listings with the most recent reviews first. The existing query may now be updated to look like this.

const cursor = client.db("sample_airbnb").collection("listingsAndReviews").find(
                        {
                            bedrooms: { $gte: minimumNumberOfBedrooms },
                            bathrooms: { $gte: minimumNumberOfBathrooms }
                        }
                    ).sort({ last_review: -1 });

There are 192 documents in the library that match the above query. Let’s imagine you don’t want the script to process that many results. Rather, you’d like to confine your findings to a smaller number of documents. You can use limit to add another sort() method to the existing query. limit(), as the name says, sets the cursor’s limit. You can now modify the query so that it only returns a set number of results.

const cursor = client.db("sample_airbnb").collection("listingsAndReviews").find(
                        {
                            bedrooms: { $gte: minimumNumberOfBedrooms },
                            bathrooms: { $gte: minimumNumberOfBathrooms }
                        }
                    ).sort({ last_review: -1 })
                    .limit(maximumNumberOfResults);

You could iterate over the cursor to get each result one at a time. Instead, you may use Cursor’s toArray() function to retrieve all of our results in an array. Your code now appears to be as follows:

const cursor = client.db("sample_airbnb").collection("listingsAndReviews").find(
                        {
                            bedrooms: { $gte: minimumNumberOfBedrooms },
                            bathrooms: { $gte: minimumNumberOfBathrooms }
                        }
                    ).sort({ last_review: -1 })
                    .limit(maximumNumberOfResults);
const results = await cursor.toArray();

Let’s wrap the query in an asynchronous function and add functionality to print the results now that you have it ready to go.

async function findListingsWithMinimumBedroomsBathroomsAndMostRecentReviews(client, {
    minimumNumberOfBedrooms = 0,
    minimumNumberOfBathrooms = 0,
    maximumNumberOfResults = Number.MAX_SAFE_INTEGER
} = {}) {
    const cursor = client.db("sample_airbnb").collection("listingsAndReviews").find(
                            {
                                bedrooms: { $gte: minimumNumberOfBedrooms },
                                bathrooms: { $gte: minimumNumberOfBathrooms }
                            }
                            ).sort({ last_review: -1 })
                            .limit(maximumNumberOfResults);

    const results = await cursor.toArray();

    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();

            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()}`);
        });
    } else {
        console.log(`No listings found with at least ${minimumNumberOfBedrooms} bedrooms and ${minimumNumberOfBathrooms} bathrooms`);
    }
}

This function can be called by passing a connected MongoClient as well as an object with properties defining the minimum number of bedrooms, bathrooms, and the total number of results.

await findListingsWithMinimumBedroomsBathroomsAndMostRecentReviews(client, {
    minimumNumberOfBedrooms: 4,
    minimumNumberOfBathrooms: 2,
    maximumNumberOfResults: 5
});

If you’ve followed the instructions in the previous section, the result should be something like this:

Found listing(s) with at least 4 bedrooms and 2 bathrooms:

1. name: Beautiful Beach House
    _id: 5db6ed14f2e0a60683d8fe44
    bedrooms: 4
    bathrooms: 2.5
    most recent review date: Mon Oct 28 2019

2. name: Spectacular Modern Uptown Duplex
    _id: 582364
    bedrooms: 4
    bathrooms: 2.5
    most recent review date: Wed Mar 06 2019

3. name: Grace 1 - Habitat Apartments
    _id: 29407312
    bedrooms: 4
    bathrooms: 2.0
    most recent review date: Tue Mar 05 2019

4. name: 6 bd country living near beach
    _id: 2741869
    bedrooms: 6
    bathrooms: 3.0
    most recent review date: Mon Mar 04 2019

5. name: Awesome 2-storey home Bronte Beach next to Bondi!
    _id: 20206764
    bedrooms: 4
    bathrooms: 2.0
    most recent review date: Sun Mar 03 2019

What is MongoDB JavaScript (NodeJS) Aggregation Operations?

In MongoDB, Aggregate Operations are expressions that may be used to obtain reduced and summarised results. The aggregate pipeline in MongoDB’s Query API lets you build a pipeline with one or more stages, each of which performs a different action on your data.

The aggregation pipeline can be compared to a car assembly line. Automobile production necessitates the use of assembly stations arranged in lines. Drills and welders are among the specialized instruments available at each station. The factory assembles and turns the raw materials and parts into completed goods.

The assembly line is the aggregation pipeline, the assembly stations are the aggregation stages, and the specialized tools are operator expressions.

What are the different MongoDB JavaScript (NodeJS) Driver Index Types?

1) Unique Indexes

Unique Indexes prevent duplicate values from being stored in the indexed fields. During the formation of a collection, MongoDB produces a unique index on the _id column by default. Set the unique option to true and indicate the field or combination of fields that you want to avoid duplication on to generate a unique index.

The createIndex() method is used in the following example to create a unique index on the theaterId column in the sample_mflix database’s theatres collection.

  const database = client.db("sample_mflix");
   const movies = database.collection("movies");


   // Create a unique index on the "theaterId" field in the "theaters" collection.
   const result = await movies.createIndex({ theaterId: 1 }, { unique: true });
   console.log(`Index created: ${result}`);

If you try to write a duplicate value that violates the unique index, MongoDB will throw an error that looks like this:

E11000 duplicate key error index

2) Geospatial Indexes

MongoDB uses 2dsphere indexes to support queries on Geospatial coordinate data. You can query Geospatial data for inclusion, intersection, and proximity using a 2dsphere index.

You must specify a field that only contains GeoJSON objects to construct a 2dsphere index. The location.geo field in the following sample document from the sample_mflix database’s theatres collection is a GeoJSON Point object that describes the theater’s coordinates:

{
  "_id" : ObjectId("59a47286cfa9a3a73e51e75c"),
  "theaterId" : 104,
  "location" : {
     "address" : {
        "street1" : "5000 W 147th St",
        "city" : "Hawthorne",
        "state" : "CA",
        "zipcode" : "90250"
     },
     "geo" : {
        "type" : "Point",
        "coordinates" : [
           -118.36559,
           33.897167
        ]
     }
  }
}

To allow geographical searches, the following example utilizes the createIndexes() method to build a 2dsphere index on the location.geo field in the theatres collection in the sample_mflix database.

const database = client.db("sample_mflix");
const movies = database.collection("movies");

// Create a 2dsphere index on the "location.geo" field in the "theaters" collection.
const result = await movies.createIndex({ "location.geo": "2dsphere" });
console.log(`Index created: ${result}`);

For computing distances on a Euclidean plane and working with the “legacy coordinate pairs” terminology used in MongoDB 2.2 and earlier, MongoDB additionally supports 2d indexes. 

3) Text Indexes

Text search queries on string content are supported by Text Indexes. Any field whose value is a string or an array of string elements can be included in these indexes. MongoDB has text search capabilities for a variety of languages. When building the index, you have the option of specifying the default language. For more details, see our guide to text search queries.

The following example creates a text index on the fullplot field in the movies collection in the sample_mflix database using the createIndex() function with english as the default language.

  const database = client.db("sample_mflix");
  const movies = database.collection("movies");


   // Create a text index on the "fullplot" field in the
   // "movies" collection.
   const result = await movies.createIndex({ fullplot: "text" }, { default_language: "english" });
   console.log(`Index created: ${result}`);

The query below is an example of one that the index constructed above would cover. Because text indexes do not provide sort order, the sort is omitted.

   const query = { $text: { $search: "java coffee shop" } };
   const projection = { fullplot: 1 };
   const cursor = movies
     .find(query)
     .project(projection);

4) Multikey Indexes

Multikey Indexes are indexes that improve query efficiency when ascending or descending indexes are used on fields with an array value. A multikey index can be created with the same syntax as a single field or compound index.

The createIndex() method is used in the following example to create an ascending index on the cast field (array of names) in the sample_mflix database’s movies collection.

   const database = client.db("sample_mflix");
   const movies = database.collection("movies");


   // Create a multikey index on the "cast" array field
   // in the "movies" collection.
   const result = await movies.createIndex({ cast: 1 });
   console.log(`Index created: ${result}`);

The query below is an example of one that the index constructed above would cover.

   const query = { cast: "Burt Reynolds" };
   const sort = { cast: 1, genre: 1 };
   const projection = { cast: 1 };


   const cursor = movies
     .find(query)
     .sort(sort)
     .project(projection);

5) Single Field Indexes

Single Field Indexes increase query performance by specifying an ascending or descending sort order on a single field of a document.

The createIndex() method is used in the following example to create an ascending order index on the title field in the sample_mflix database’s movies collection.

   const database = client.db("sample_mflix");
   const movies = database.collection("movies");


   // Create an ascending index on the "title" field in the
   // "movies" collection.
   const result = await movies.createIndex({ title: 1 });
   console.log(`Index created: ${result}`);

The query below is an example of one that the index constructed above would cover.

   const query = { title: "Batman" }
   const sort = { title: 1 };
   const projection = { title: 1 };


   const cursor = movies
     .find(query)
     .sort(sort)
     .project(projection);

6) Compound Indexes

Compound Indexes are indexes that improve query performance when various fields of a document are sorted in ascending or descending order. Each field in the index must have its own direction (ascending or descending).

The createIndex() method is used in the following example to construct a compound index on the type and genre fields in the sample_mflix database’s movies collection.

   const database = client.db("sample_mflix");
   const movies = database.collection("movies");


   // Create an ascending index on the "type" and "genre" fields
   // in the "movies" collection.
   const result = await movies.createIndex({ type: 1, genre: 1 });
   console.log(`Index created: ${result}`);

The query below is an example of one that the index constructed above would cover.

   const query = { type: "movie", genre: "Drama" };
   const sort = { type: 1, genre: 1 };
   const projection = { type: 1, genre: 1 };


   const cursor = movies
     .find(query)
     .sort(sort)
     .project(projection);

Conclusion

You have learned about the core steps required to be done in MongoDB Javascript (NodeJS) Connector in this article. After that, you looked at how to use MongoDB Javascript (NodeJS) CRUD Operations and indexes. Setting MongoDB Javascript Connector manually can be challenging especially for a beginner & this is where Hevo saves the day.

Visit our Website to Explore Hevo

Hevo Data, a No-code Data Pipeline provides you with a consistent and reliable solution to manage data transfer between a variety of sources like MongoDB and a wide variety of Desired Destinations, with a few clicks. Hevo Data with its strong integration with 100+ sources (including 40+ free sources) allows you to not only export data from your desired data sources & load it to the destination of your choice, but also transform & enrich your data to make it analysis-ready so that you can focus on your key business needs and perform insightful analysis using BI tools.

Want to take Hevo for a spin? Sign Up for a 14-day free trial and experience the feature-rich Hevo suite first hand. You can also have a look at the unbeatable pricing that will help you choose the right plan for your business needs.

Share your experience of learning about MongoDB Javascript (NodeJS)! Let us know in the comments section below!

No-code Data Pipeline for MongoDB