This tutorial was written by Abdulrasaq Jamiu Adewuyi.
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 utilized 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.
Overview of the technologies and tools that will be used
If you’d like to follow along, here’s what I’ll be using.
Web application framework
NextJS: We will be using NextJS to build our application. NextJS is a popular JavaScript framework for building server-rendered and fullstack 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.
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
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 source code on GitHub.
git clone https://github.com/jaymeeu/nextjs-atlas-search.git
cd nextjs-atlas-search
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.
- Sign up for a free MongoDB account
- Create a cluster
- Add a database user
- Configure a network connection
- Load sample data
- Get connection string
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 NextJS application
Open a new terminal window, install all the necessary dependencies for this project, and initiate the project by running the npm command. Let’s also create a .env.local
file and update it with the MONGODB_URI (your connection string).
cd ../mdbsearch
npm install
touch .env
Create a .env.local
file, and update as below:
MONGODB_URI=mongodb+srv://user:password@cluster0.xxxxx.mongodb.net
Start the application by running the below command.
npm run dev
Once the application starts running, you should see the page below running at http://localhost:3000.
With this data loading from the MongoDB database, next, let's proceed to implement the search functionality.
Implementing text search in our application with MongoDB Altas Search
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
. The 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.
At this point, scroll to the bottom of the screen and click on 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 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
, turkey
, 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 search 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.
Creating a route in the NextJS to handle search requests
Now that we have the search index configured, let’s create an API route that will handle the search request. Open /src/app/api/search/[search]/route.js
file update with the below code.
import clientPromise from "../../../../lib/mongodb";
export async function GET(request, {params}) {
const client = await clientPromise;
const db = client.db("sample_airbnb");
let itemCollection = await db.collection("listingsAndReviews");
const stringQueries = (await params).search
const queries = JSON.parse(stringQueries)
// Start building the search aggregation stage
let searcher_aggregate = {
"$search": {
"index": 'search_home',
"compound": {
"must": [
// get home where queries.category is property_type
{ "text": {
"query": queries.category,
"path": 'property_type',
"fuzzy": {}
}},
// get home where queries.country is address.country
{"text": {
"query": queries.country,
"path": 'address.country',
"fuzzy": {}
}}
]}
}
};
// A projection will help us return only the required fields
let projection = {
'$project': {
'accommodates': 1,
'price': 1,
'property_type': 1,
'name': 1,
'description': 1,
'host': 1,
'address': 1,
'images': 1,
"review_scores": 1
}
};
// We can now execute the aggregation pipeline, and return the first 50 elements
let results = await itemCollection.aggregate([ searcher_aggregate, projection ]).limit(50).toArray();
return new Response(JSON.stringify(results),{
status: 200,
headers: { "Content-Type": "application/json" },
})
}
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
.
We don’t necessarily want to send all the fields back to the front end, so we use a projection stage to limit the data we send over.
Finally, we use the aggregate
method to run this aggregation pipeline, and return the first 50 results to the front end.
The result of the pipeline will be returned when a request is made to /api/search/:search
.
At this point, we have an endpoint that can be used to search for homes by their country.
Implement search feature in our frontend application
From our project folder, open the mdbsearch/components/Header/index.js
file. Find the searchNow
function and update it with the below code.
//Search function goes here
const searchNow = async (e) => {
setshow(false)
let search_params = JSON.stringify({
country: country,
category: `${activeCategory}`
})
setLoading(true)
await fetch(`/api/search/${search_params}`)
.then((response) => response.json())
.then(async (res) => {
updateCategory(activeCategory, res)
router.query = { country, category: activeCategory };
setcountryValue(country);
router.push(router);
})
.catch((err) => console.log(err))
.finally(() => setLoading(false))
}
Next, find the handleChange
function. Let's update it with the below code:
const handleChange = async (e) => {
//Autocomplete function goes here
setCountry(e.target.value);
}
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.
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.
Add autocomplete to search box
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.
Implement autocomplete API in our application
With this done, let’s update our API as below:
Open the /src/app/api/country/autocomplete/[param]/route.js
file, and update it with the below code:
import clientPromise from "../../../../../lib/mongodb";
export async function GET(request, {params}) {
const client = await clientPromise;
const db = client.db("sample_airbnb");
let itemCollection = await db.collection("listingsAndReviews");
const queries = (await params).param
let results = await itemCollection.aggregate(
[
{
'$search': {
'index': 'country_autocomplete',
'autocomplete': {
'query': queries,
'path': 'address.country',
},
'highlight': {
'path': [ 'address.country']
}
}
}, {
'$limit': 1
}, {
'$project': {
'address.country': 1,
'highlights': {
'$meta': 'searchHighlights'
}
}
}
]).toArray();
return new Response(JSON.stringify(results),{
status: 200,
headers: { "Content-Type": "application/json" },
})
}
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.
//Autocomplete function goes here
const handleChange = async (e) => {
setCountry(e.target.value);
if(e.target.value.length > 1){
await fetch(`/api/country/autocomplete/${e.target.value}`)
.then((response) => response.json())
.then(async (res) => {
setsug_countries(res)
})
}
else{
setsug_countries([])
}
}
The above function will make a request to the /api/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.
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.
Top comments (0)