Querying MongoDB
So now that we've put a lot of documents into our database, it's time to query our database to get information out of it. There are a variety of reasons and methods you can query a database. You could be querying a database to get one a specific record, like if you're querying for a user object to show a profile page, or you could be querying for a bunch of things at once, like if a user was searching for pets and you wanted to show five results.
findOne
Let's start with basic querying. Run this in the console.
db.pets.findOne();
This will go out and find the first item in the database that matches your query and return that. In this case we didn't give it a query which is the same as saying "give me anything." You'll probably get back the first item you inserted into your database which in my case is going to be a Havanese dog named Luna.
Okay, so let's we want to find the 1337 item we put in the database. Try this:
db.pets.findOne({ index: 1337 });
This will find you one item (of which there is only one anyway) where the index is equal to 1337. Okay, one more, let's find one where it's a dog named Spot.
db.pets.findOne({ name: "Spot", type: "dog" });
find
So far we've only been looking at finding one record at time. If you want to find all documents that match your query, you'll have to use find instead of findOne. Let's try it.
db.pets.find({ type: "dog" });
Notice this only giving you twenty results. What MongoDB does by default is only hand you twenty results at a time in the form of what's called an iterator or a cursor. An iterator will give you twenty records at a time and allow you to iterate over your results. Frequently this is a useful pattern because you could in theory want to iterate over your whole collection and this allows you to do it piece by piece.
Try iterating now. Run this:
it
it
it
You'll notice that you're getting twenty different records each time. it
will tell your iterator that you want it to iterate again on the last iterator you queried for.
count, limit, and toArray
Okay, so let's try this:
db.pets.count({ type: "dog" }); // probably pretty big number
db.pets.find({ type: "dog" }).limit(40);
it; // after this the cursor will end
count
lets you figure our how many of something there are. limit
lets the cursor know when you want to stop. But what if you just want to get everything all at once? Try toArray
db.pets.find({ type: "dog" }).limit(40).toArray();
This will just dump everything out into an array which is nice if you just want everything all at once. There's a myriad of other things you can do that determine how the data is returned but you can find those fairly easily when you need them.
Query operators
What if we wanted to find all senior-aged cats in our data set? MongoDB will let you do that! Let's try this
db.pets.count({ type: "cat", age: { $gt: 12 } });
Note you can use these query operators anywhere you're providing a query e.g. findOne, find, etc. The ones you're primarily going to be interested in are
- $gt - greater than
- $gte - greater than or equal to
- $lt - less than
- $lte - less than or equal to
- $eq - equals (usually not necessary)
- $ne - not equals
- $in - has the value in the array (MongoDB can store arrays and objects too!)
- $nin - does not have the value in the array
If you wanted to see all Fidos that are not dogs you could do:
db.pets.find({
type: { $ne: "dog" },
name: "Fido",
});
Logical operators
Taking it a step further, you can do logical operators as well. Let's say you want to find birds between 4 and 8 years old
db.pets.find({
type: "bird",
$and: [{ age: { $gte: 4 } }, { age: { $lte: 8 } }],
});
You also have $or, $nor, and $not available to you. Keep in mind that $not and $ne are different. The former is the logical operator and the latter is !==
.
Special operators
These won't be useful now but I just wanted to let you know that you can query by type (see if something is a number, array, object, etc.) with $type and you can query if a document has a field or not with $exists.
There's a bunch more you can do too. MongoDB even has [geospatial operators][geo] so you can query if two points on the globe are close to each other!
Sorts
Frequently you'll need to do different sorts with your queries. Let's say you're making a search page for these pets (foreshadowing what we're about to do!) Maybe the person who searching on your website is a kind soul and looking to adopt some senior dogs. They could say sort by age, descending.
db.pets.find({ type: "dog" }).sort({ age: -1 });
The -1
means descending and, as you probably guessed, 1 means ascending.
Projections
Lastly, to conclude our little lesson querying (there's still a lot more you can do), let's talk about projections. The simplest way to use projections is just to limit which fields you return.
db.pets.find({ type: "dog" }, { name: 1, breed: 1 });
The 1
means "definitely include this". In thise case, we're only including name and breed. If you leave something out (like age) then it doesn't come along for the ride. Notice that _id
does come along. If you don't want that, you have to explictly exclude it
db.pets.find({ type: "dog" }, { name: 1, breed: 1, _id: 0 });
db.pets.find({ type: "dog" }, { name: true, breed: true, _id: false }); // note that true and false work too
It also works to just exclude fields which means include everything I haven't excluded
db.pets.find({ type: "dog" }, { _id: 0 });