Menu
Share on

Introduction

Querying documents is an essential skill necessary to do many different operations within MongoDB. You need to be able to query to effectively retrieve the documents you need, to update existing information within your databases, and to understand commonalities and differences between your documents.

In this guide, we'll cover the basics of how to compose queries for MongoDB to help you retrieve documents according to your requirements. We will show you how queries work on a general level, then we will explore various operators that MongoDB provides to help you narrow down results by evaluating your conditions.

RELATED ON PRISMA.IO

If you are using Prisma Client with MongoDB, you can use the MongoDB connector (currently in preview) to connect and manage your data.

Create example collections

Throughout this article, we'll use a collection called students and a collection called teachers, both stored inside of a database called school.

You can create the example database and populate the collections using the following commands:

use school
db.students.insertMany([
{
first_name: "Ashley",
last_name: "Jenkins",
dob: new Date("January 08, 2003"),
grade_level: 8
},
{
first_name: "Brian",
last_name: "McMantis",
dob: new Date("September 18, 2010"),
grade_level: 2
},
{
first_name: "Leah",
last_name: "Drake",
dob: new Date("October 03, 2009")
},
{
first_name: "Naomi",
last_name: "Pyani"
},
{
first_name: "Jasmine",
last_name: "Took",
dob: new Date("April 11, 2011")
},
{
first_name: "Michael",
last_name: "Rodgers",
dob: new Date("February 25, 2008"),
grade_level: 6
},
{
first_name: "Toni",
last_name: "Fowler"
}
])
db.teachers.insertMany([
{
first_name: "Nancy",
last_name: "Smith",
subjects: [
"vocabulary",
"pronunciation"
]
},
{
first_name: "Ronald",
last_name: "Taft",
subjects: [
"literature",
"grammar",
"composition"
]
},
{
first_name: "Casey",
last_name: "Meyers",
subjects: [
"literature",
"composition",
"grammar"
]
},
{
first_name: "Rebecca",
last_name: "Carrie",
subjects: [
"grammar",
"literature"
]
},
{
first_name: "Sophie",
last_name: "Daggs",
subjects: [
"literature",
"composition",
"grammar",
"vocabulary",
"pronunciation"
]
}
])

Basic querying syntax

Now that you have two collections with documents in them, you can experiment with how to retrieve individual documents or groups of documents. The main way to fetch documents from MongoDB is by calling the find() method on the collection in question.

For instance, to collect all of the documents from the students collection, you can call find() with no arguments:

db.students.find()
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }
{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

To make the output more readable, you can also chain the pretty() method after find():

db.<collection>.find().pretty()
{
"_id" : ObjectId("60e8743b4655cbf49ff7cb83"),
"first_name" : "Ashley",
"last_name" : "Jenkins",
"dob" : ISODate("2003-01-08T00:00:00Z"),
"grade_level" : 8
}
{
"_id" : ObjectId("60e875d54655cbf49ff7cb84"),
"first_name" : "Brian",
"last_name" : "McMantis",
"dob" : ISODate("2010-09-18T00:00:00Z"),
"grade_level" : 2
}
{
"_id" : ObjectId("60e875d54655cbf49ff7cb85"),
"first_name" : "Leah",
"last_name" : "Drake",
"dob" : ISODate("2009-10-03T00:00:00Z")
}
{
"_id" : ObjectId("60e877914655cbf49ff7cb86"),
"first_name" : "Naomi",
"last_name" : "Pyani"
}
{
"_id" : ObjectId("60e8792d4655cbf49ff7cb87"),
"first_name" : "Jasmine",
"last_name" : "Took",
"dob" : ISODate("2011-04-11T00:00:00Z")
}
{
"_id" : ObjectId("60e8792d4655cbf49ff7cb88"),
"first_name" : "Michael",
"last_name" : "Rodgers",
"dob" : ISODate("2008-02-25T00:00:00Z"),
"grade_level" : 6
}
{
"_id" : ObjectId("60e8792d4655cbf49ff7cb89"),
"first_name" : "Toni",
"last_name" : "Fowler"
}

You can see that an _id field has been added to each of the documents. MongoDB requires a unique _id for each document in a collection. If you do not provide one upon object creation, it will add one for you. You can use this ID to retrieve a single object reliably:

db.student.find(
{
_id : ObjectId("60e8792d4655cbf49ff7cb89")
}
)
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

Filter results by equality

You can filter results by checking for equality by providing an object that specifies field and value pairs that you want to look for.

For instance, you can get a list of the students named "Brian" with the following query:

db.students.find({first_name: "Brian"})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }

Any qualities that you specify using the field-value notation will be interpreted as an equality query. If you give multiple fields, all of the values must be equal for a document to match.

For instance, if we perform the same equality match as before, but include grade_level as 3, no documents will be returned:

db.students.find({first_name: "Brian", grade_level: 3})

Filtering using comparison operators

While the simple equality filtering is useful, it is fairly limited in what it can express. For other types of comparisons, MongoDB provides various comparison operators so that you can query in other ways.

The basic function of the available comparison operators is likely fairly familiar if you work with other programming languages. Most operators work by passing an object to the field name that contains the operator and the value you want to compare against, like this:

<field_name>: { <operator>: <value_to_compare_against> }

Equal to

The $eq operator checks for equality between the value provided and the field values in the documents. In most cases, this has the same functionality as the equality comparisons we used above.

For example, we can express the same query for students named "Brian" by typing:

db.students.find({
first_name: { $eq: "Brian" }
})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }

Not equal to

You can also query for documents that are not equal to a provided value. The operator for this is $ne.

For instance, one way to find all students who have a grade_level set is to search for entries where the field is not set to null:

db.students.find({
grade_level: { $ne: null }
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

Greater than

The $gt operator allows you to query for documents where the field value is greater than the provided reference number.

For instance, to find all records for students in in grades higher than grade 6, we could type:

db.students.find({
grade_level: { $gt: 6 }
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }

Greater than or equal to

The $gte operator expresses a query for values that are the same as or greater than the provided value.

We can conduct the same query as above but additionally include students in grade 6 by typing:

db.students.find({
grade_level: { $gte: 6 }
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

Less than

Use the $lt operator to find values less than the supplied value.

For instance, we can view birth dates before January 1, 2010 by typing:

db.students.find({
dob: { $lt: new Date("January 1, 2010") }
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

Less than or equal to

The $lte operator checks for values less than or equal to the reference provided.

For instance, find students in grade 6 and lower, type:

db.students.find({
grade_level: { $lte: 6 }
})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

Match any of a group of values

The $in operator works like the $eq equality operator, but allows you to provide multiple possible values in an array. For instance, instead of checking whether a field value is equal to 8, it can check whether the value is any of [8, 9, 10, 11].

The $in operator also works with regular expressions. For example, we can find all students whose first name ends with either an 'i' or an 'e' by typing:

db.students.find({
first_name: {
$in: [
/i$/,
/e$/
]
}
})
{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

Match none of a group of values

The inverse of the above procedure is to find all documents that have values not in a given array. The operator for that is $nin.

For instance, we can find all students who have first names that don't end in 'i' or 'e' by typing:

db.students.find({
first_name: {
$nin: [
/i$/,
/e$/
]
}
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

Filtering using logical operators

To form more complex queries, you can compose multiple conditions using logical operators. Logical operators work by passing them an object of an expression or an array containing multiple objects of expressions.

The logical AND operator

The $and operator will return results that satisfy all of the expressions that have been passed to it. Every expression within the $and expression must evaluate to true in order to be returned.

For example, you can use $and to query for students that have both a birth date and a grade level set:

db.students.find({
$and: [
{ dob: { $ne: null } },
{ grade_level: { $ne: null } }
]
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

The logical OR operator

The $or operator performs a logical OR calculation. If any of the expressions that are passed to it are true, the entire clause is considered satisfied.

You can use this, for example, to query students who are missing either of the fields we queried for above:

db.students.find({
$or: [
{ dob: { $eq: null } },
{ grade_level: { $eq: null } }
]
})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }
{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

The logical NOT operator

The $not operator negates the value of the expression that is passed to it. Instead of operating on an array of expressions, since $not is a unary operator, it operates on a single single defining an operator expression directly.

This leads to a slightly different syntax than the previous operators. Instead of wrapping a full field and value expression, you use $not as part of the value of the field match and it takes only an operator expression as its argument rather than a full expression (the field name is outside of the $not expression instead of inside of it).

For instance, we can find all students who do not have a birthday before 2010 by typing. This differs from checking for dob entries that are less than 2010 because it also returns any documents that do not have that field set at all:

db.students.find({
dob: {
$not: {
$lt: new Date("January 1, 2010")
}
}
})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

The logical NOR operator

The $nor operator takes an array of objects and returns documents that do not match any of the conditions specified in those objects. Only documents that fail all of the conditions will be returned.

For example, if you want to retrieve documents of students who are not in grade 6 who also do not have a last name that ends in 's', you could type:

db.students.find({
$nor: [
{ grade_level: 6 },
{ last_name: /s$/ }
]
})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }
{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

Filtering on existence

Some other ways to test are based on the state of a field or value.

For instance, the $exists filter checks for the existence of a field within a document. You can set $exists to true or false to determine which documents to retrieve.

For instance, if you wanted to find student documents that have a grade level, you can type:

db.students.find({
grade_level: { $exists: true }
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

Filtering based on array characteristics

You can also query documents through the arrays they hold. There are a number of operators that can be used to match based on array elements or other qualities.

Specifying required elements

The $all operator returns documents that have an array containing all of the elements given.

For example, if you want to retrieve only teachers that teach both composition and grammar, you could type:

db.teachers.find({
subjects: {
$all: [ "composition", "grammar" ]
}
})
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "composition" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "composition", "grammar" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "composition", "grammar", "vocabulary", "pronunciation" ] }

Multiple requirements for one element

The $elemMatch operator returns documents if the array being tested contains at least one element that satisfies all of the conditions provided.

As a pretty useless example, to return documents for teachers who teach a subject that's alphabetically between "literature" and "vocabulary", you could type:

db.teachers.find({
subjects: {
$elemMatch: {
$gt: "literature",
$lt: "vocabulary"
}
}
})
{ "_id" : ObjectId("60eddca65eb74f5c676f3baa"), "first_name" : "Nancy", "last_name" : "Smith", "subjects" : [ "vocabulary", "pronunciation" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "composition", "grammar", "vocabulary", "pronunciation" ] }

Both of the teachers who teach "pronunciation" are listed here, as that's the only element that satisfies both conditions.

Querying by array size

Finally, you can use the $size operator to query for documents of a certain size. For instance, to find all of the teachers who teach three subjects, type:

db.teachers.find({
subjects: { $size: 3 }
})
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "composition" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "composition", "grammar" ] }

Conclusion

In this guide, we've covered how to query for documents with MongoDB databases. We covered the basic way that the find() method works and how to make its output more readable. Afterwards, we took a look at many of the operators that MongoDB provides to specify the exact parameters of the documents you are interested in.

Understanding how to compose queries to narrow down results and pick out documents that match your specifications is important both when reading and updating data. By getting familiar with the various ways that operators can be chained together, you can express complex requirements that match different types of documents.

RELATED ON PRISMA.IO

If you are using Prisma Client with MongoDB, you can use the MongoDB connector (currently in preview) to connect and manage your data.