Grokking MongoDB
Vote

0% completed

​

What is a MongoDB Query?

Writing a Filter

Sorting and Limiting

Choosing Fields

How the Parts Work Together

Reading Large Results

Calculations on the Results

Key Takeaways

db.users.find({ age: { $gte: 30 } })
[ { _id: 2, name: 'Bob', age: 30, email: 'bob@example.com' }, { _id: 3, name: 'Charlie', age: 35, email: 'charlie@example.com' } ]

That line is a MongoDB query, a request to retrieve data from a collection. Its argument states which documents you want, and here $gte means "greater than or equal to", which is why the server returned the users aged 30 or older. The argument is called a query filter: a document that describes the condition each result must meet.

To run the query yourself, first create the three users. The drop() removes any earlier users collection, and only these three documents then exist.

db.users.drop() db.users.insertMany([ { _id: 1, name: "Alice", age: 25, email: "alice@example.com" }, { _id: 2, name: "Bob", age: 30, email: "bob@example.com" }, { _id: 3, name: "Charlie", age: 35, email: "charlie@example.com" } ])

Writing a Filter

The simplest filter names a field and a value, and it matches documents where the field equals that value. The comparison on strings is case sensitive, so the first query below returns Bob and the second returns nothing.

db.users.find({ name: "Bob" }) db.users.find({ name: "bob" })

An empty filter, or no filter at all, matches every document. A filter with several fields requires all of them to match. This query again returns only Bob, because Charlie is old enough but has a different name:

db.users.find({ age: { $gte: 30 }, name: "Bob" })

When no document matches, find() returns an empty result and does not raise an error. The related method findOne() returns the first matching document directly, not inside an array, and it returns null when nothing matches.

Sorting and Limiting

The filter decides which documents to return, but it says nothing about their order. Without a sort, MongoDB does not guarantee any order, even when the results appear to follow the order of insertion. To request one, add sort(), where 1 means ascending and -1 means descending.

db.users.find({ age: { $gte: 30 } }).sort({ age: -1 })
[ { _id: 3, name: 'Charlie', age: 35, email: 'charlie@example.com' }, { _id: 2, name: 'Bob', age: 30, email: 'bob@example.com' } ]

limit() sets the maximum number of results, and combining the two gives the oldest user who matches:

db.users.find({ age: { $gte: 30 } }).sort({ age: -1 }).limit(1)
[ { _id: 3, name: 'Charlie', age: 35, email: 'charlie@example.com' } ]

The order in which you write these two methods does not change the result. Writing .limit(1).sort({ age: -1 }) also returns Charlie, because the server always sorts before it limits.

Choosing Fields

So far each result has contained the whole document. A second argument to find(), called a projection, selects the fields to return. Set a field to 1 to include it. The _id field is included unless you set it to 0.

db.users.find({ age: { $gte: 30 } }, { name: 1, _id: 0 })
[ { name: 'Bob' }, { name: 'Charlie' } ]

You can also set a field to 0 to exclude it and return the rest. Other than for _id, you cannot do both in one projection, and { name: 1, email: 0 } fails with Cannot do exclusion on field email in inclusion projection.

Requesting only the fields you need is more than a matter of neat output. When documents are large, a projection reduces the data that the server sends over the network, and your application has less to process.

How the Parts Work Together

A single query can use all of these parts. This one returns the name of the oldest user aged 30 or older:

db.users.find({ age: { $gte: 30 } }, { name: 1, _id: 0 }).sort({ age: -1 }).limit(1)
[ { name: 'Charlie' } ]

The sort can use age even though the projection removes age from the output, because the result is the same as if the server applied the steps in the order shown here.

The three users pass through the filter, the sort, the limit and the projection, leaving only the name Charlie.
The three users pass through the filter, the sort, the limit and the projection, leaving only the name Charlie.

Reading Large Results

find() does not return all of its documents at once. It returns a cursor, which is an object that gives you results as you ask for them. In mongosh, a collection with 25 documents therefore prints the first 20 followed by a message:

Type "it" for more

Typing it prints the remaining 5. In a program, you loop over the cursor instead.

Calculations on the Results

find() returns documents, not calculations. If you only need the number of matching documents, use countDocuments(). For calculations across documents, such as an average, use aggregate().

db.users.countDocuments({ age: { $gte: 30 } }) db.users.aggregate([{ $group: { _id: null, averageAge: { $avg: "$age" } } }])
2 [ { _id: null, averageAge: 30 } ]

Key Takeaways

  • A query filter is a document that describes which documents to return, and all of its conditions must match.
  • Without sort(), the order of results is not guaranteed.
  • sort() always runs before limit(), whichever you write first.
  • A projection selects fields, and _id is included unless you exclude it.

Reading Progress

0%


Vote for new content

On This Page

Writing a Filter

Sorting and Limiting

Choosing Fields

How the Parts Work Together

Reading Large Results

Calculations on the Results

Key Takeaways