Quick Start: Node.js & MongoDB - How to Read Documents

Lauren Schaefer

#Node.js
Quick Start Node.js and MongoDB

Welcome (or welcome back!) to the MongoDB and Node.js Quick Start series! In the first post, I walked you through how to connect to a MongoDB database using a Node.js script. In the second post, I built on code we created in the first post to show you how to create MongoDB documents from inside of a Node.js script.

Today, we’ll build on what we’ve done already and move on to the next CRUD operation: read. We’ll work through the basics of querying for one document and querying for many documents.

Get started with an M0 cluster on Atlas today. It's free forever, and it’s the easiest way to try out the steps in this blog series.

Find a Document

Let’s begin by querying for an Airbnb listing in the listingsAndReviews collection by name. If you’re new to this series, check out the previous post where we dive into the details of how data is stored in this sample database.

We can query for a document by calling Collection’s findOne(). findOne() will return the first document that matches the given query. Even if more than one document matches the query, only one document will be returned.

findOne() has only one required parameter: a query of type object. The query object can contain zero or more properties that MongoDB will use to find a document in the collection. If you want to query all documents in a collection without narrowing your results in any way, you can simply send an empty object.

Since we want to search for an Airbnb listing with a particular name, we will include the name field in the query object we pass to findOne(): findOne({ name: nameOfListing }).

Our function to find a listing by querying the name field could look something like the following:

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

We can call this function by passing a connected MongoClient as well as the name of a listing we want to find. Let's search for a listing named "Infinite Views" that we created in a previous post.

await findOneListingByName(client, "Infinite Views");

If you've created the documents as described in the previous post in this blog series, the output would be something like the following.

Found a listing in the db 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 }

Note that the _id of the document in your database will not match the _id in the sample output above. For more details on _id, see the previous post in this series.

Find Multiple Documents

Now that you know how to query for one document, let’s discuss how to query for multiple documents at a time. We can do so by calling Collection’s find().

Similar to findOne(), the first parameter for find() is the query object. You can send zero to many properties inside the query object.

Let’s say we want to search for all Airbnb listings that have minimum numbers of bedrooms and bathrooms. We could do so by making a call like the following:

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

As you can see above, we have two properties in our query object: one for bedrooms and one for bathrooms. We can leverage the $gte comparison query operator to search for documents that have bedrooms greater than or equal to a given number. We can do the same to satisfy our minimum number of bathrooms requirement. MongoDB provides a variety of other comparison query operators that you can utilize in your queries. See the official documentation for more details.

The query above will return a Cursor. A Cursor allows traversal over the result set of a query.

You can also use Cursor’s functions to modify what documents are included in the results. For example, let’s say we want to sort our results so that those with the most recent reviews are returned first. We could use Cursor’s sort() function to sort the results using the last_review field. We could sort the results in descending order (indicated by passing -1 to sort()) so that listings with the most recent reviews will be returned first. We can now update our existing query to look like the following.

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

The above query matches 192 documents in our collection. Let’s say we don’t want to process that many results inside of our script. Instead, we want to limit our results to a smaller number of documents. We can chain another of sort()’s functions to our existing query: limit(). As the name implies, limit() will set the limit for the cursor. We can now update our query to only return a certain number of results.

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

We could choose to iterate over the cursor to get the results one by one. Instead, if we want to retrieve all of our results in an array, we can call Cursor’s toArray() function. Now our code looks like the following:

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

Now that we have our query ready to go, let’s put it inside an asynchronous function and add functionality to print the results.

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

We can call this function by passing a connected MongoClient as well as an object with properties indicating the minimum number of bedrooms, the minimum number of bathrooms, and the maximum number of results.

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

If you've created the documents as described in the previous post in this blog series, the output would be something like the following:

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

Wrapping Up

This post included many code snippets that built on code written in the first post of this MongoDB and Node.js Quick Start series. To get a full copy of the code used in today’s post, visit the Node.js Quick Start GitHub Repo.

Be on the lookout for the next post in this series where we’ll work through the next of the CRUD operations: update.

Series versions

The examples in this article were created with the following application versions:

ComponentVersion used
MongoDB4.0
MongoDB Node.js Driver3.3.2
Node.js10.16.3

All posts in the Quick Start: Node.js and MongoDB series: