Explore Developer Center's New Chatbot! MongoDB AI Chatbot can be accessed at the top of your navigation to answer all your MongoDB questions.

Introducing MongoDB 8.0, the fastest MongoDB ever!
MongoDB Developer
Atlas
plus
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right
Productschevron-right
Atlaschevron-right

Adding Autocomplete To Your NextJS Applications With Atlas Search

Abdulrasaq Jamiu Adewuyi11 min read • Published Feb 28, 2023 • Updated Feb 28, 2023
AtlasSearchJavaScript
Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty

Introduction

Imagine landing on a webpage with thousands of items and you have to scroll through all of them to get what you are looking for. You will agree that it's a bad user experience. For such a website, the user might have to leave for an alternative website, which I'm sure is not what any website owner would want.
Providing users with an excellent search experience, such that they can easily search for what they want to see, is crucial for giving them a top-notch user experience.
The easiest way to incorporate rich, fast, and relevant searches into your applications is through MongoDB Atlas Search, a component of MongoDB Atlas. 

Explanation of what we will be building

In this guide, I will be showing you how I created a text search for a home rental website and utilize Atlas Search to integrate full-text search functionality, also incorporating autocomplete into the search box.
This search will give users the ability to search for homes by country.
Let's look at the technology we will be using in this project.

Overview of the technologies and tools that will be used

If you'd like to follow along, here's what I'll be using.

Frontend framework

NextJS: We will be using NextJS to build our front end. NextJS is a popular JavaScript framework for building server-rendered React applications.
I chose this framework because it provides a simple setup and helps with optimizations such as automatic code splitting and optimized performance for faster load times. Additionally, it has a strong community and ecosystem, with a large number of plugins and examples available, making it an excellent choice for building both small- and large-scale web applications.

Backend framework

NodeJS and ExpressJS: We will be using these to build our back end. Both are used together for building server-side web applications.
I chose these frameworks because Node.js is an open-source, cross-platform JavaScript runtime environment for building fast, scalable, and high-performance server-side applications. Express.js, on the other hand, is a popular minimal and flexible Node.js web application framework that provides a robust set of features for building web and mobile applications.

Database service provider

MongoDB Atlas is a fully managed cloud database service provided by MongoDB. It's a cloud-hosted version of the popular NoSQL database (MongoDB) and offers automatic scalability, high availability, and built-in security features. With MongoDB Atlas, developers can focus on building their applications rather than managing the underlying infrastructure, as the service takes care of database setup, operation, and maintenance.
MongoDB Atlas Search is a full-text search and analytics engine integrated with MongoDB Atlas. It enables developers to add search functionality to their applications by providing fast and relevant search results, including text search and faceted search, and it also supports autocomplete and geospatial search.
MongoDB Atlas Search is designed to be highly scalable and easy to use.

Pre-requisites

The full source of this application can be found on Github.

Project setup

Let's get to work!

Setting up our project

To start with, let's clone the repository that contains the starting source code on Github.
1git clone https://github.com/mongodb-developer/search-nextjs/
2cd search-nextjs
Once the clone is completed, in this repository, you will see two sub-folders:
mdbsearch: Contains the Nextjs project (front end) backend: Contains the Node.js project (back end)
Open the project with your preferred text editor. With this done, let's set up our MongoDB environment.

Setting up a MongoDB account

To set up our MongoDB environment, we will need to follow the below instructions from the MongoDB official documentation.
Your connection string should look like this: mongodb+srv://user:password@cluster0.xxxxx.mongodb.net

Identify a database to work with

We will be working with the sample-airbnb sample data from MongoDB for this application because it contains appropriate entries for the project.
If you complete the above steps, you should have the sample data loaded in your cluster. Otherwise, check out the documentation on how to load sample data.

Start the Node.js backend API

The API for our front end will be provided by the Node.js back end. To establish a connection to your database, let's create a .env file and update it with the connection string.
1cd backend
2npm install
3touch .env
Update .env as below
1PORT=5050
2MONGODB_URI=<CONNECTION_STRING>
To start the server, we can either utilize the node executable or, for ease during the development process, use nodemon. This tool can automatically refresh your server upon detecting modifications made to the source code. For further information on tool installation, visit the official website.
Run the below code
1npx nodemon .
This command will start the server. You should see a message in your console confirming that the server is running and the database is connected. 

Start the NextJs frontend application

With the back end running, let's start the front end of your application. Open a new terminal window and navigate to the mdbsearch folder. Then, install all the necessary dependencies for this project and initiate the project by running the npm command. Let's also create a .env file and update it with the backend url.
1cd ../mdbsearch
2npm install
3touch .env
Create a .env file, and update as below:
1NEXT_PUBLIC_BASE_URL=http://localhost:5050/
Start the application by running the below command.
1npm run dev
Once the application starts running, you should see the page below running at http://localhost:3000. The running back end is already connected to our front end, hence, during the course of this implementation, we need to make a few modifications to our code.
Application home page.
With this data loading from the MongoDB database, next, let's proceed to implement the search functionality.
To be able to search through data in our collection, we need to follow the below steps:

Create a search index

The MongoDB free tier account gives us the room to create, at most, three search indexes.
From the previously created cluster, click on the Browse collections button, navigate to Search, and at the right side of the search page, click on the Create index button. On this screen, click Next to use the visual editor, add an index name (in our case, search_home), select the listingsAndReviews collection from the sample_airbnb database, and click Next.
From this screen, click on Refine Your Index. Here is where we will specify the field in our collection that will be used to generate search results. In our case --- address and property_type --- address field is an object that has a country property, which is our target.
Therefore, on this screen, we need to toggle off the Enable Dynamic Mapping option. Under Field Mapping, click the Add Field Mapping button. In the Field Name input, type address.country, and in the Data Type, make sure String is selected. Then, scroll to the bottom of the dialog and click the Add button. Create another Field Mapping for property_type. Data Type should also be String.
The index configuration should be as shown below.
MongoDB atlas search index configuration page
At this point, scroll to the bottom of the screen and click on Save Changes. Then, click the Create Search Index button. Then wait while MongoDB creates your search index. It usually takes a few seconds to be active. Once active, we can start querying our collection with this index.
You can find detailed information on how to create a search index in the documentation.

Testing our search index

MongoDB provides a search tester, which can be used to test our search indexes before integrating them into our application. To test our search index, let's click the QUERY button in the search index. This will take us to the Search Tester screen.
Remember, we configure our search index to return results from address.country or property_type. So, you can test with values like spain, brazil, ​​apartment, etc. These values will return results, and we can explore each result document to see where the result is found from these fields.
Test with values like span and brasil. These will return no data result because it's not an exact match. MongoDB understands that scenarios like these are likely to happen. So, Atlas Search has a fuzzy matching feature. With fuzzy matching, the search tool will be searching for not only exact matching keywords but also for matches that might have slight variations, which we will be using in this project. You can find the details on fuzzy search in the documentation.
With the search index created and tested, we can now implement it in our application. But before that, we need to understand what a MongoDB aggregation pipeline is.

Consume search index in our backend application

Now that we have the search index configured, let's try to integrate it into the API used for this project. Open backend/index.js file, find the comment Search endpoint goes here , and update it with the below code.
Start by creating the route needed by our front end.
1// Search endpoint goes here
2app.get("/search/:search", async (req, res) => {
3  const queries = JSON.parse(req.params.search)
4 // Aggregation pipeline goes here
5 });
In this endpoint, /search/:search we create a two-stage aggregation pipeline: $search and $project. $search uses the index search_home, which we created earlier. The $search stage structure will be based on the query parameter that will be sent from the front end while the $project stage simply returns needed fields from the $search result.
This endpoint will receive the country and property_type, so we can start building the aggregation pipeline. There will always be a category property. We can start by adding this.
1// Start building the search aggregation stage
2let searcher_aggregate = {
3    "$search": {
4      "index": 'search_home',
5      "compound": {
6        "must": [
7          // get home where queries.category is property_type
8        { "text": {
9          "query": queries.category,
10          "path": 'property_type',
11          "fuzzy": {}
12        }},
13
14// get home where queries.country is address.country
15        {"text": {
16          "query": queries.country,
17          "path": 'address.country',
18          "fuzzy": {}
19        }}
20      ]}
21    }
22  };
We don't necessarily want to send all the fields back to the frontend, so we can use a projection stage to limit the data we send over.
1app.get("/search/:search", async (req, res) => {
2 const queries = JSON.parse(req.params.search)
3  // Start building the search aggregation stage
4  let searcher_aggregate = { ...  };
5  // A projection will help us return only the required fields
6  let projection = {
7          '$project': {
8            'accommodates': 1,
9            'price': 1,
10            'property_type': 1,
11            'name': 1,
12            'description': 1,
13            'host': 1,
14            'address': 1,
15            'images': 1,
16            "review_scores": 1
17          }
18        };
19 });
Finally, we can use the aggregate method to run this aggregation pipeline, and return the first 50 results to the front end.
1app.get("/search/:search", async (req, res) => {
2  const queries = JSON.parse(req.params.search)
3   // Start building the search aggregation stage
4  let searcher_aggregate = { ... };
5 // A projection will help us return only the required fields
6  let projection = { ... };
7 // We can now execute the aggregation pipeline, and return the first 50 elements
8  let results = await itemCollection.aggregate([ searcher_aggregate, projection ]).limit(50).toArray();
9  res.send(results).status(200);
10 });
The result of the pipeline will be returned when a request is made to /search/:search.
At this point, we have an endpoint that can be used to search for homes by their country.
The full source of this endpoint can be located on Github .

Implement search feature in our frontend application

From our project folder, open the mdbsearch/components/Header/index.js file.Find the searchNowfunction and update it with the below code.
1//Search function goes here
2    const searchNow = async (e) => {
3        setshow(false)
4        let search_params = JSON.stringify({
5            country: country,
6            category: `${activeCategory}`
7        })
8        setLoading(true)
9        await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}search/${search_params}`)
10            .then((response) => response.json())
11            .then(async (res) => {
12                updateCategory(activeCategory, res)
13                router.query = { country, category: activeCategory };
14                setcountryValue(country);
15                router.push(router);
16            })
17            .catch((err) => console.log(err))
18            .finally(() => setLoading(false))
19    }
NextFind the handleChangefunction, let update it with the below code 
1  const handleChange = async (e) => {
2 //Autocomplete function goes here
3 setCountry(e.target.value);
4}
With the above update, let's explore our application. Start the application by running npm run dev in the terminal. Once the page is loaded, choose a property type, and then click on "search country." At the top search bar, type brazil. Finally, click the search button. You should see the result as shown below.
Application search result page.
The search result shows data where address.country is brazil and property_type is apartment. Explore the search with values such as braz, brzl, bral, etc., and we will still get results because of the fuzzy matching feature.
Now, we can say the experience on the website is good. However, we can still make it better by adding an autocomplete feature to the search functionality.
Most modern search engines commonly include an autocomplete dropdown that provides suggestions as you type. Users prefer to quickly find the correct match instead of browsing through an endless list of possibilities. This section will demonstrate how to utilize Atlas Search autocomplete capabilities to implement this feature in our search box.
In our case, we are expecting to see suggestions of countries as we type into the country search input. To implement this, we need to create another search index.
From the previously created cluster, click on the Browse collections button and navigate to Search. At the right side of the search page, click on the Create index button. On this screen, click Next to use the visual editor, add an index name (in our case, country_autocomplete), select the listingsAndReviews collection from the sample_airbnb database, and click Next.
From this screen, click on Refine Your Index. We need to toggle off the Enable Dynamic Mapping option.
Under Field Mapping, click the Add Field Mapping button. In the Field Name input, type address.country, and in the Data Type, this time, make sure Autocomplete is selected. Then scroll to the bottom of the dialog and click the Add button.
At this point, scroll to the bottom of the screen and Save Changes. Then, click the Create Search Index button. Wait while MongoDB creates your search index --- it usually takes a few seconds to be active.
Once done, we should have two search indexes, as shown below.
Atlas search page showing the created search indexes

Implement autocomplete API in our backend application

With this done, let's update our backend API as below:
Open the backend/index.js file, and update it with the below code:
1//Country autocomplete endpoint goes here
2app.get("/country/autocomplete/:param", async (req, res) => {
3  let  results = await itemCollection.aggregate(
4      [
5        {
6          '$search': {
7            'index': 'country_autocomplete',
8            'autocomplete': {
9              'query': req.params.param,
10              'path': 'address.country',
11            },
12            'highlight': {
13              'path': [ 'address.country']
14            }
15          }
16        }, {
17          '$limit': 1
18        }, {
19          '$project': {
20            'address.country': 1,
21            'highlights': {
22              '$meta': 'searchHighlights'
23            }
24          }
25        }
26      ]).toArray();
27
28  res.send(results).status(200);
29 });
The above endpoint will return a suggestion of countries as the user types in the search box. In a three-stage aggregation pipeline, the first stage in the pipeline uses the $search operator to perform an autocomplete search on the address.country field of the documents in the country_autocomplete index. The query parameter is set to the user input provided in the URL parameter, and the highlight parameter is used to return the matching text with highlighting.
The second stage in the pipeline limits the number of results returned to one.
The third stage in the pipeline uses the $project operator to include only the address.country field and the search highlights in the output

Implement autocomplete in our frontend application

Let's also update the front end as below. From our project folder, open the mdbsearch/components/Header/index.js file. Find the handeChange function, and let's update it with the below code.
1//Autocomplete function goes here
2    const handleChange = async (e) => {
3            setCountry(e.target.value);
4            if(e.target.value.length > 1){
5                await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}country/autocomplete/${e.target.value}`)
6                .then((response) => response.json())
7                .then(async (res) => {
8                    setsug_countries(res)
9                })
10            }
11            else{
12                setsug_countries([])
13            }
14    }
The above function will make a HTTP request to the country/autocomplete and save the response in a variable.
With our code updated accordingly, let's explore our application. Everything should be fine now. We should be able to search homes by their country, and we should get suggestions as we type into the search box.
showing text autocomplete.
Voila! We now have fully functional text search for a home rental website. This will improve the user experience on the website.

Summary

To have a great user experience on a website, you'll agree with me that it's crucial to make it easy for your users to search for what they are looking for. In this guide, I showed you how I created a text search for a home rental website with MongoDB Atlas Search. This search will give users the ability to search for homes by their country.
MongoDB Atlas Search is a full-text search engine that enables developers to build rich search functionality into their applications, allowing users to search through large volumes of data quickly and easily. Atlas Search also supports a wide range of search options, including fuzzy matching, partial word matching, and wildcard searches. Check out more on MongoDB Atlas Search from the official documentation.
Questions? Comments? Let's continue the conversation! Head over to the MongoDB Developer Community --- we'd love to hear from you.

Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Related
Tutorial

How to Write Unit Tests for MongoDB Atlas Functions


Sep 09, 2024 | 10 min read
Article

Capturing and Storing Real-World Optics With MongoDB Atlas, OpenAI GPT-4o, and PyMongo


Sep 04, 2024 | 7 min read
Tutorial

Supercharge Your AI Applications: AWS Bedrock, MongoDB, and TypeScript


Oct 10, 2024 | 9 min read
Podcast

Atlas 5-Year Anniversary Podcast Series Episode 1 - Onramp to Atlas


Aug 17, 2023 | 22 min
Table of Contents