MongoDB find() operation fetches all the fields of a document. However, to get only a few selected fields in a query result, MongoDB provides a feature called projections.
In this article, we explore what are projections and how they are used.
What is MongoDB projection?
MongoDB projection is a filter that retrieves only the selected fields in a query result. For example, if you want to get only the name and email id of a few customers from your customer database, getting other details like date of birth, address, and many other fields is uncalled for. You can think of the empty find() as the ‘*’ operator of SQL query and projections as similar to mentioning specific comma-separated fields in SQL query.
Using Projection in Mongo Shell
Let us consider the following document taken from the customer collection of the sample_analytics database on MongoDB Atlas free tier.
{
"_id": {"$oid":"5ca4bbcea2dd94ee58162a6b"},
"username":"serranobrian",
"name":"Leslie Martinez",
"address":"Unit 2676 Box 9352\nDPO AA38560",
"birthdate":{"$date":{"$numberLong":"154708220000"}},
"email":"tcrawford@gmail.com",
"accounts":[{"$numberInt":"170945"},{"$numberInt":"951849"}],
"tier_and_details":{"a15baf69a759423297f11ce6c7b0bc9a":
{"tier":"Platinum","benefits":["airline lounge access"],
"Active":true,"id":"a15baf69a759423297f11ce6c7b0bc9a"}
}
}
The basic find() query that will fetch entire document is as follows:
db.customers.find({"username": "serranobrian"})
Let us say, you want only the username, name and email. This is where we should use projections within the second enclosed braces inside find():
db.customers.find({"username": "serranobrian"}, {name: 1, email: 1, username: 1})
Note that the fields that we want are marked as 1. This is the basic syntax of projections.
The output of the above query is:
[
{
_id: ObjectId("5ca4bbcea2dd94ee58162a6b"),
username: 'serranobrian',
name: 'Leslie Martinez',
email: 'tcrawford@gmail.com'
}
]
In the above output, we also got the _id field. Unless explicitly specified, _id appears in all the query results by default, as it is the unique identifier for a MongoDB document.
To eliminate the _id field, we can mark it as 0:
db.customers.find({"username": "serranobrian"}, {name: 1, email: 1, username: 1, _id: 0})
You will now get only the three specified fields:
[
{
username: 'serranobrian',
name: 'Leslie Martinez',
email: 'tcrawford@gmail.com'
}
]
If you want all the fields except one, let’s say, accounts, we can simply mark that as 0, and all the other fields will be displayed in the result.
Projections for embedded documents
To get the values of fields inside an object, you can use the dot.notation. To demonstrate this, we will use the movies collection of the sample_mflix database.
In this collection, the awards field is an object, containing fields like wins, nominations and text. To get the ‘wins’ field for the movie title ‘Miss Lulu Bett’, we would query the database as:
db.movies.find({"title": "Miss Lulu Bett"}, {"awards.wins": 1})
<meta charset="utf-8" />Output:
[ { _id: ObjectId("573a1391f29313caabcd71e3"), awards: { wins: 1 } } ]
Getting arrays is same as any other field:
db.movies.find({"title": "Miss Lulu Bett"}, {"cast": 1})
Output:
[
{
_id: ObjectId("573a1391f29313caabcd71e3"),
cast: [
'Lois Wilson',
'Milton Sills',
'Theodore Roberts',
'Helen Ferguson'
]
}
]
Using project() in application code
Applications can use the project() method to fetch the specific fields. For example, in Node.js, to fetch the win field of awards array, from the movie collection, we can write the code as:
const cursor = db
.collection('movies')
.find({
title: "Miss Lulu Bett"
})
.project({ "awards.wins": 1 });
In Java, the code is similar, but the method used is projection:
findWins = collection.find(eq("title", "Miss Lulu Bett")).projection(include("awards.wins"));
Using $project in MongoDB aggregation framework
You can apply projections as the last stage of MongoDB aggregation pipeline by using the $project operator.
db.movies.aggregate( [{$project: {“title” : 1, “cast” : 1}}]}
$project stage does not support array indexes. However, you can project different fields into a single array. For example, consider the below data:
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "name" : “Sam”, "age" : 21 }
You can project it as:
db.collection.aggregate( [ { $project: { myArray: [ "$name", "$age" ] } } ] )
The $ operator indicates the value of the field. Hence, the field values of name and age will be displayed in the array myArray:
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ Sam, 21 ] }
Operators used in MongoDB projection
You can use many operators to filter out data and improve performance. MongoDB supports the $, $elemMatch, $slice and $meta operators for the same.
$projection
$ operator is used in projection to get the first element of an array that matches the condition given in the query. Read more about $projection and the rules to use it from the link.
$elemMatch
$elemMatch also limits the query result to the first element of the array, but needs one or more conditions to execute. Learn more about $elemMatch from the MongoDB documentation.
$slice
Do not confuse the $slice projection operator with the $slice aggregation operator. The $slice project operator specifies the number of elements in an array to return in the query result. You can specify a positive number to return the first few elements and a negative number to start from the last. Learn more about $slice.
$meta
$meta returns the metadata of the document, using the keyword provided. The metadata keyword can be textScore and indexKey. MongoDB Atlas search provides additional $meta keywords like searchScore and searchHighlights. Learn about $meta.
Summary
Projections are a great way to filter data. Just like we specify column names in the SQL query, we specify the field names along with a boolean to project data that we want to. You can learn more about projection operators in the MongoDB official documentation page.