Introduction
In this lab, you will learn the fundamentals of using MongoDB indexes to optimize query performance. An index is a special data structure that holds a small, easy-to-search portion of a collection's data, enabling MongoDB to find documents much faster than scanning the entire collection.
You will begin by observing the performance of a query without an index. Then, you will create single-field and compound indexes and see how they dramatically improve query and sorting speeds. Finally, you will learn how to manage your indexes by listing and removing them. By the end of this lab, you will have a practical understanding of how to create and use indexes to make your MongoDB applications more efficient.
Querying Without an Index
Before creating an index, it is important to understand how MongoDB performs without one. In this step, you will set up a sample collection, run a query, and analyze its execution plan to see the performance impact of a full collection scan.
First, open the MongoDB Shell (mongosh) to interact with your database. This command-line interface allows you to execute commands directly against your MongoDB instance.
mongosh
Once inside the shell, you will see the > prompt. Let's switch to a new database named indexlab and insert some sample documents into a users collection. If the database or collection does not exist, MongoDB will create them automatically.
use indexlab
db.users.insertMany([
{ name: "Alice", age: 28, city: "New York" },
{ name: "Bob", age: 35, city: "San Francisco" },
{ name: "Charlie", age: 42, city: "Chicago" },
{ name: "David", age: 25, city: "New York" },
{ name: "Eve", age: 31, city: "San Francisco" }
]);
Now, let's find all users older than 30. We will use the .explain("executionStats") method to see how MongoDB executes this query. This method provides detailed statistics about the query's execution plan.
db.users.find({ age: { $gt: 30 } }).explain("executionStats");
The output provides detailed statistics about the query's execution. Look for the winningPlan and executionStats sections.
Example output (truncated):
{
"queryPlanner": {
"winningPlan": {
"stage": "COLLSCAN",
"filter": { "age": { "$gt": 30 } },
"direction": "forward"
}
},
"executionStats": {
"executionSuccess": true,
"nReturned": 3,
"executionTimeMillis": 0,
"totalKeysExamined": 0,
"totalDocsExamined": 5
}
}
The key information here is stage: "COLLSCAN" and totalDocsExamined: 5.
COLLSCANstands for "Collection Scan." It means MongoDB had to inspect every single document in the collection to find the ones that match the query.totalDocsExamined: 5confirms that all 5 documents in the collection were scanned.
While this is fast for a small collection, a collection scan on millions of documents would be very slow. In the next step, you will fix this by adding an index.
Creating and Using a Single-Field Index
Now that you have seen the inefficiency of a collection scan, let's improve performance by creating an index. An index on the age field will allow MongoDB to quickly find the relevant documents without scanning the entire collection.
You should still be in the mongosh shell from the previous step.
Create an index on the age field in ascending order. The 1 specifies an ascending index, while -1 would specify a descending one.
db.users.createIndex({ age: 1 });
MongoDB will confirm that the index was created successfully. The default name for this index will be age_1.
Now, run the exact same query as in the previous step and examine its execution plan.
db.users.find({ age: { $gt: 30 } }).explain("executionStats");
Example output (truncated):
{
"queryPlanner": {
"winningPlan": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"keyPattern": { "age": 1 },
"indexName": "age_1"
}
}
},
"executionStats": {
"executionSuccess": true,
"nReturned": 3,
"executionTimeMillis": 0,
"totalKeysExamined": 3,
"totalDocsExamined": 3
}
}
Notice the significant changes in the execution plan:
- The
stageis nowIXSCAN, which stands for "Index Scan." This indicates that MongoDB used theage_1index to find the matching documents. totalKeysExaminedandtotalDocsExaminedare now 3, not 5. MongoDB only had to look at the 3 documents that matched the query via the index, ignoring the other 2. This is the source of the performance gain.
Using a Compound Index for Sorting
Indexes are not just for speeding up queries; they are also crucial for efficient sorting. When you sort on a field that is not indexed, MongoDB must perform the sort in memory, which can be slow and consume significant RAM. A compound index, which includes multiple fields, can optimize queries that filter and sort on those fields.
Let's create a compound index on the city (ascending) and age (descending) fields. The order of fields in the index is important for how it can be used.
db.users.createIndex({ city: 1, age: -1 });
Now, let's run a query that sorts the users by city and then by age. We will use .explain() again to confirm the index is used for sorting.
db.users.find().sort({ city: 1, age: -1 }).explain("executionStats");
Example output (truncated):
{
"queryPlanner": {
"winningPlan": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"keyPattern": { "city": 1, "age": -1 },
"indexName": "city_1_age_-1"
}
}
}
}
The IXSCAN stage shows that MongoDB used our new city_1_age_-1 index. Because the data is already ordered in the index according to our sort criteria, MongoDB does not need to perform a separate, costly sorting step in memory.
To see the actual sorted result, run the query without .explain().
db.users.find().sort({ city: 1, age: -1 });
Output:
[
{ _id: ObjectId("..."), name: 'Charlie', age: 42, city: 'Chicago' },
{ _id: ObjectId("..."), name: 'Alice', age: 28, city: 'New York' },
{ _id: ObjectId("..."), name: 'David', age: 25, city: 'New York' },
{ _id: ObjectId("..."), name: 'Bob', age: 35, city: 'San Francisco' },
{ _id: ObjectId("..."), name: 'Eve', age: 31, city: 'San Francisco' }
]
The documents are correctly sorted first by city alphabetically and then by age from oldest to youngest within each city, matching the compound index definition.
Managing and Removing Indexes
While indexes improve read performance, they are not free. They consume storage space and add a small amount of overhead to write operations (inserts, updates, and deletes). Therefore, it is good practice to periodically review and remove indexes that are no longer used.
First, you can list all indexes on a collection using the getIndexes() method.
db.users.getIndexes();
Output:
[
{ "v": 2, "key": { "_id": 1 }, "name": "_id_" },
{ "v": 2, "key": { "age": 1 }, "name": "age_1" },
{ "v": 2, "key": { "city": 1, "age": -1 }, "name": "city_1_age_-1" }
]
This shows the default index on the _id field, which is created automatically for every collection, and the two indexes we created.
Let's say we have determined that the compound index city_1_age_-1 is no longer needed. You can remove it using the dropIndex() method, passing the index name as an argument.
db.users.dropIndex("city_1_age_-1");
MongoDB will return an object indicating how many indexes existed before the drop operation.
{ "nIndexesWas": 3, "ok": 1 }
Now, verify that the index has been removed by listing the indexes again.
db.users.getIndexes();
Output:
[
{ "v": 2, "key": { "_id": 1 }, "name": "_id_" },
{ "v": 2, "key": { "age": 1 }, "name": "age_1" }
]
As you can see, the city_1_age_-1 index is gone. Proper index management is a key part of maintaining a healthy and performant database.
To exit the MongoDB shell, you can type exit or press Ctrl+D.
exit;
Summary
In this lab, you have learned the essential techniques for using MongoDB indexes. You started by observing a COLLSCAN on a query without an index and understood its performance limitations. You then created a single-field index, which changed the query plan to a much more efficient IXSCAN.
Furthermore, you explored compound indexes and saw how they can be used to optimize sorting operations, avoiding costly in-memory sorts. Finally, you learned how to manage your indexes by listing them with getIndexes() and removing unused ones with dropIndex(). These skills are fundamental to building fast and scalable applications with MongoDB.

