You've Been Writing Your MongoDB Queries the Wrong Way

You've Been Writing Your MongoDB Queries the Wrong Way

By Jordan Lamoreaux

November 7, 2023

A simple question: How do you get all documents where the property is a reference to another document?

For example:

const rebelAlliance = await affiliation.findOne({ name: 'Rebel Alliance' }); 
const characters = await character.find({ affiliation: { name: 'Rebel Alliance' } });

This is perfectly valid and is a common approach. Choosing whether to pass the full document or just the ID for a MongoDB query is an important decision that can impact the performance and functionality of your application. In this post, we'll explore the pros and cons of each approach and help you make an informed decision.

The Case for Passing the Full Document

One advantage of passing the full document as a query parameter is that it can simplify your code and reduce the number of database queries. If you have a large number of fields in your document and you need to perform multiple queries that rely on different fields, it can be more efficient to retrieve the full document once and then filter the results in your code.

For example, let's say you have a characters collection that includes a name field and an affiliation field that references an affiliations collection. If you want to retrieve all characters with a certain affiliation, you could pass the full affiliation document as a query parameter:

const rebelAlliance = await affiliation.findOne({ name: 'Rebel Alliance' }); 
const characters = await character.find({ affiliation:  rebelAlliance});

This would retrieve all characters with an affiliation field that matches the Rebel Alliance document. By passing the full document, you avoid the need to perform an additional query to retrieve the _id of the Rebel Alliance document.

The Case for Passing the ID

On the other hand, passing just the ID as a query parameter can have performance benefits and is generally recommended for larger collections. When you pass just the ID, MongoDB can use an index on the _id field to quickly retrieve the document. This can be much faster than scanning the entire collection for matching documents.

In addition, passing just the ID can help avoid issues with consistency and concurrency. If you pass the full document as a query parameter, there's a chance that the document may have been updated or deleted by another process between the time you retrieve the document and the time you use it in your query. By passing just the ID, you can ensure that you're always querying the most up-to-date version of the document.

For example, if you have a characters collection with millions of documents, it may be more efficient to pass just the _id of the Rebel Alliance document as a query parameter:

const rebelAlliance = await affiliation.findOne({ name: 'Rebel Alliance' });
const characters = await character.find({ affiliation: rebelAlliance._id });

This would first retrieve the Rebel Alliance document from the affiliations collection, and then use its `_id` field to retrieve all characters with that `_id` value in their affiliation field.

Conclusion

When you pass the full document as a query parameter, it can simplify your code and reduce the number of queries needed, but it can also lead to issues with consistency if the document is updated or deleted by another process while you are using it in your query.

On the other hand, passing just the ID can help avoid consistency issues and provide better performance, especially for larger collections. Additionally, MongoDB can use an index on the _id field to quickly retrieve the document, which can be much faster than scanning the entire collection for matching documents.