DEV Community

Cover image for How to Optimize MongoDB Query Performance with Indexes
VisuaLeaf
VisuaLeaf

Posted on • Originally published at visualeaf.com

How to Optimize MongoDB Query Performance with Indexes

Not all slow MongoDB queries are bad queries.

Sometimes the query is fine, but MongoDB does not have a good index to help with your filtering, sorting, and retrieving of the data

In this tutorial, we'll use payments as an example of our database. The collection starts without a useful index for our query. We'll identify the slow operation, learn about the recommended index, explain why the compound index works, and manage it visually in VisuaLeaf.

Payments collection in VisuaLeaf showing currency status amount and paidAt fields

The workflow is simple:

slow query -> query profiler -> index recommendation -> compound index -> index manager

You can use the same workflow when your own MongoDB collections start to feel slow.

On this page

  1. The Slow Query Problem
  2. The Payments Query We Want to Optimize
  3. Find the Slow Query in VisuaLeaf
  4. Read the Index Recommendation
  5. Why This Compound Index Works
  6. Check and Manage Indexes
  7. Indexing Mistakes to Avoid
  8. FAQ

The Slow Query Problem

MongoDB can return results quickly when a collection is small.

But as the collection grows, the same query can become slower. MongoDB may need to scan many documents, sort a large result set, or check fields that are not indexed.

That extra work is the real problem.

For example, this query looks simple:

db.payments.find({
  status: "paid"
})
Enter fullscreen mode Exit fullscreen mode

If the status field has no useful index; MongoDB may scan the collection to find matching documents.

This is called a collection scan.

MongoDB Explain Plan showing COLLSCAN for payments query filtered by status paid

A collection scan is not always bad. On a tiny collection, it may not matter. On a large collection used by your app every day, it matters a lot.

When you inspect a query plan, these stages are important:

A common warning sign looks like this:

Stage Meaning
COLLSCAN MongoDB scanned the collection
IXSCAN MongoDB used an index
FETCH MongoDB fetched documents after using an index
SORT MongoDB performed a sort operation
totalDocsExamined: 50000
nReturned: 25
Enter fullscreen mode Exit fullscreen mode

That means MongoDB checked 50,000 documents to return only 25.

The goal is not just to make the query look cleaner. The goal is to make MongoDB do less work.

MongoDB explains the technical index behavior in the official MongoDB Indexes documentation.

The Payments Query We Want to Optimize

Now let’s use a more realistic query.

Imagine you have a payments collection, and you often need to find *'paid'* USD payments above a certain amount, sorted by the newest payment date.

The query looks like this:

db.payments.find({
  currency: "USD",
  status: "paid",
  amount: { $gte: 100 }
}).sort({
  paidAt: -1
})
Enter fullscreen mode Exit fullscreen mode

This query does four things:

If your payments collection has no useful index for this pattern, MongoDB has to work harder than necessary.

Field What the query does
currency Keeps only payments in USD
status Keeps only paid payments
amount Keeps payments greater than or equal to 100
paidAt Sorts newest payments first

A single-field index can help with simple filters, but this query uses multiple fields.

That is why a compound index makes more sense here. It can support the filter, sort, and range condition together.

We will let VisuaLeaf recommend the exact index after we inspect the slow query.

MongoDB documents compound indexes in the official Compound Indexes documentation.

Find the Slow Query in VisuaLeaf

Do not create indexes because a field looks important.

Find the slow query first. Then index the query pattern.

Use the same payments query from the previous section in the Visual Query Builder. Add the filters for currency, status, and amount, then sort by paidAt descending.

In VisuaLeaf, the Explain view shows the execution plan, scanned documents, returned documents, execution time, and index usage.

MongoDB Explain Plan in VisuaLeaf Query Builder showing collection scan and documents examined

If MongoDB scans many documents and returns only a few, the query probably needs a better index.

You can also run the same check manually with explain():

db.payments.find({
  currency: "USD",
  status: "paid",
  amount: { $gte: 100 }
}).sort({
  paidAt: -1
}).explain("executionStats")
Enter fullscreen mode Exit fullscreen mode

When you read the result, check these values:

Metric What you check
totalDocsExamined How many documents MongoDB scanned
nReturned How many documents MongoDB returned
executionTimeMillis How long the query took
winningPlan Which execution plan MongoDB selected

Look for IXSCAN when MongoDB uses an index, and COLLSCAN when MongoDB scans the collection. For one query, the Explain view is enough.

For repeated slow operations across collections, use VisuaLeaf Query Profiling. The profiler helps you see slow operations over time, not just one query you are testing manually.

MongoDB Query Profiling Dashboard in VisuaLeaf showing slow operations

MongoDB also provides database profiling features for slow operations. You can read more in the official Database Profiler documentation.

Read the Index Recommendation

After you find the slow query, check which index would actually help.

In this example, the payments collection has no useful index for the query. VisuaLeaf detects repeated collection scans and recommends a compound index based on the fields used by the query.

MongoDB index recommendations in VisuaLeaf showing the recommended compound index for the payments collection

The recommendation for the compound index is:

db.payments.createIndex({
  currency: 1,
  status: 1,
  paidAt: -1,
  amount: 1
})
Enter fullscreen mode Exit fullscreen mode

This is useful because the recommendation does not only say β€œadd an index.”

It shows the exact fields and the order.

That order is the important part.

Why This Compound Index Works

The recommended index matches the way the query filters and sorts the data.

After creating the recommended index, run the same query again in the Explain view.

MongoDB Explain Plan in VisuaLeaf showing IXSCAN and compound index usage for payments query

After creating the compound index, VisuaLeaf shows that MongoDB uses the index for the payments query.

Now MongoDB uses the compound index instead of scanning the full collection.

The query first filters by currency and status, then sorts by paidAt, and also applies a range condition on amount.

In simple terms:

Field Why it is in the index
currency Exact filter
status Exact filter
paidAt Sort by newest payment
amount Range condition

Check and Manage Indexes

After you create an index, check what exists in the collection.

Indexes are easy to add and easy to forget.

Over time, a collection can collect old indexes, duplicate indexes, or indexes created for queries that no longer exist.

Use the Index Manager to review the indexes on your collection.

MongoDB Index Manager in VisuaLeaf showing indexes for a payments collection

The Index Manager helps you check the index name, fields, type, size, usage, properties, and status.

This helps you avoid creating the same index twice.

It also helps you review old indexes when your application queries change.

You can combine this workflow with the Visual Query Builder, MongoDB Shell, Aggregation Pipeline Builder, and Charts and Dashboards when you need to test, analyze, and present your MongoDB data.

If you prefer, you can create the index visually from the Index Manager instead of writing the command manually.

Create MongoDB index visually in VisuaLeaf Index Manager with field direction and index options

Indexing Mistakes to Avoid

Indexes help when they match your workload.

They create problems when you add them without a reason.

Creating indexes for every field

Do not index every field in the collection.

Each index needs storage. Each index also adds work when MongoDB inserts, updates, or deletes documents.

Create indexes for queries your application actually runs.

Ignoring the sort

A query may filter quickly but still sort slowly.

Example:

db.payments.find({
  status: "paid"
}).sort({
  paidAt: -1
})
Enter fullscreen mode Exit fullscreen mode

A useful index is:

db.payments.createIndex({
  status: 1,
  paidAt: -1
})
Enter fullscreen mode Exit fullscreen mode

This supports the filter and the sort.

Putting fields in the wrong order

A compound index is not only about choosing the right fields. The order matters too.

For the payments query, starting with the *amount* is not as useful as starting with the *currency* and *status, because the *amount** is a range condition.

Same fields. Different order. Different performance.

Keeping indexes you no longer use

Your application changes.

Your queries change.

Your indexes should change too.

Review unused indexes from time to time and remove the ones that no longer support real queries.

Conclusion

MongoDB query optimization starts with the query pattern.

Do not guess. Find the slow query first. Check whether MongoDB scans too many documents. Then create an index that matches how your query filters, sorts, and ranges over the data.

For simple filters, a single field index may be enough.

For queries like the payments example, a compound index is usually better.

With VisuaLeaf, you can detect slow queries, review index recommendations, and manage MongoDB indexes visually in one place.

Top comments (0)