MongoDB – Projection

By using MongoDB projection, we can control the display of data at the front end, thus enhancing its actual use in real-world scenarios. In recent years, MongoDB has reached several milestones. This has enhanced the adoption of MongoDB and several companies are in search of full-stack developers with MongoDB expertise

Instead of selecting all of the data from the document, we can use MongoDB projection to select specific data (which is deemed necessary). Consider this: if you have 10 fields in your document and only need 5, you will use MongoDB projection operators to select these 5 fields.

Projection is most useful when choosing the required data from entire documents, especially for operators such as MongoDB $, $elemMatch, $slice, and $Meta. Through projection, we can improve database performance.

The MongoDB projection is a query that allows us to specify which fields we want to return. We can do projection in MongoDB by appending a 0 or 1 to the name of the fields, including a query. If parameter 1 is specified, it will be displayed, while parameter 0 will be hidden.

Example of MongoDB Projection:

Let’s understand MongoDB projection with an example. The following documents are provided in a collection called student data. 

> db.studentdata.find().pretty() 
{ 
        "_id" : ObjectId("59bf61230be1d6660c3982af"), 
        "student_name" : "Carlos", 
        "student_id" : 2000, 
        "student_age" : 20 
} 
{ 
        "_id" : ObjectId("59bf61500be1d6660c3982b0"), 
        "student_name" : "Ben", 
        "student_id" : 2001, 
        "student_age" : 21 
} 
{ 
        "_id" : ObjectId("59bf61650be1d6660c3982b1"), 
        "student_name" : "Jimmy", 
        "student_id" : 2002, 
        "student_age" : 20 
}

We’ll use the projection like this to obtain only the student_id for all documents:

> db.studentdata.find({}, {"_id": 0, "student_id": 1})
{ "student_id" : 2000}
{ "student_id" : 2001}
{ "student_id" : 2002}

Value 1 means the field is shown and Value 0 means the field is not shown. In Projection, when we set a field to 1, other fields are automatically set to 0, except _id, so we must set 0 in the projection to avoid _id.

Sometimes you may get errors while using projection in the query. This is done if some of the fields are set to 0 and some are set to 1, i.e. mix inclusion and exclusion, the only exception is the _id field.

A query that would produce an error:

db.studentdata.find({}, {"_id": 0, "student_name": 0, "student_age": 1})

We have set student_name to 0 and other field student_age to 1. This is because we cannot mix them. You can set the fields you don’t want to display to 0 and set the fields you do want to display to 1. 

Syntax:

With the db.collection.find() method, you can use projection. The second parameter in this approach is to show which fields are returned in the corresponding documents by means of the projection parameter. 

db.collection.find({}, {field1: value1, field2: value2, ..})

  • If the field value is set to 1 or true, the field will be included in the return document.
  • If the field value is set to 0 or false, it means that the field does not appear in the return document.
  • The use of projection operators is permitted, but the find() method cannot support $, $elemMatch, $slice, and $meta projection operators.
  • The method find() always returns _id unless you set _id field to 0. There is no need to set _id to 1 for returning _id.

Usage Considerations:

The $ operator projects in a set the first matching array element of any document based on certain requirements of the query statement.
The $elemMatch projection operator takes an explicit condition argument. This enables you to project in embedded documents based on multiple fields or on a condition not found in the query.

$ Operator:

The $ operator is used to restrict an array’s contents to project the first element to fit the query condition on the array field. The first element will be returned in the specified array when no query condition is present.

Syntax:

db.collection.find( { <array>: <condition> ... }, { "<array>.$": 1 } )

Limitations:

  • In a single query, only one $ operator can be used.
  • The question must consist of only one condition in the array field in which it is applied.
  • Before the $-operator, the sort() function is used for the find() method. The order of the sort may not be correctly expressed by this function.
  • Only at the end of a field path can the $ operator be implemented.
  • The expression $slice can’t be used as a part of the same expression with $ Operator.
  • For example, the following projection may lead to undefined behavior:
db.collection.find( { <array>: <value1>, <someOtherArray>: <value2> },
                    { "<array>.$": 1 } )

The query document should contain only one condition in the array field to which it is applied. Multiple conditions can intersect and lead to undefined behavior.

Use the $elemMatch query operator to define criteria for multiple fields of documents in the array.

$elemMatch Operator:

The $elemMatch operator will restrict the array content to the first element matching a particular condition. This requirement is different from the $ operator since an explicit condition argument is needed for the $elemMatch projection operator.

An explicit condition argument is used for the $elemMatch projection operator. This allows you to project on the basis of a condition, not in your query or to project in embedded documents on the basis of multiple fields.

Syntax:

db.collection.find( { <array>: <condition> … }, { “<array>.$elemMatch”: ($elemMatch operator) } )

Example:

db.players.insert( {
   name: “ben”,
   games: [ { game: “cricket”, score: 8 }, { game: “xyz”, score: 5 } ],
   joined: new Date(“2021-02-01”),
   lastLogin: new Date(“2021-05-01”)
} )
The projection returns the field of games though the field before joined and lastLogin fields are listed in the document:

db.players.find( {}, { games: { $elemMatch: { score: { $gt: 5 } } }, joined: 1, lastLogin: 1 } )

The operation returns the following document:
{
   “_id” : ObjectId(“5edef64a1c099fff6b033977”),
   “joined” : ISODate(“2020-01-01T00:00:00Z”),
   “lastLogin” : ISODate(“2020-05-01T00:00:00Z”),
   “games” : [ { “game” : “cricket”, “score” : 8 } ]
}

Limitations:

  • Regardless of how fields are placed in the document, the last field in the document would be the field on which $elemMatch is applied.
  • Find() on MongoDB views does not support the operator for $elemMatch projection.
  • The $elemMatch operator does not accept $text query expressions.

$slice Projection Operator:

The $slice operator determines how many elements the query output should return.

When the $slice projection is in the exclusion projection, other fields of the nested document are returned in the operation.

Syntax:

db.collection.find( <query>,  { <array Field>: { $slice: <number> } })

Limitations:

  • The $slice operator can only return the sliced element. No other object in an array will be returned.
  • Since the MongoDB constraint is used, the $slice operator should not be used along with the $ projection operator, while top-level fields have $ (dollar sign) as a part of the name of a field.
  • The $slice of the array and a field embedded in the array cannot be contained in queries for the same statement to prevent MongoDB path collisions.
    For example, the following operation is invalid:

db.inventory.find( { “instock.qty”: { $gt: 25 } }, { “instock.$”: { $slice: 1 } } )

In earlier versions, MongoDB has a limitation that high-ranking field names cannot begin with the dollar ($) symbol.

$meta Operator:

The $meta operator is used to obtain a document’s metadata. For both MongoDB aggregation and MongoDB projection, the $meta operator can be used.

Syntax:

{ $meta: <metaDataKeyword> }

Usage in Projection:

  • The expression { $meta: “textScore” } can be used as part of the text score metadata of a projection document.
  • Inclusion or exclusion projection may include the $meta expression.
  • The metadata “textScore” sorts in descending order.
  • To use the { $meta: “textScore” } expression in a sorting procedure, set the name of the field to an arbitrary one. The query system does not take account of the field name.
  • For debugging purposes only, and not for application logic, the { $meta: “indexKey”} expression is used
  • The {$meta:”indexKey”} expression is preferable over the cursor.returnKey expression ().

Project Fields to Return from Query:

All fields in matching documents return queries at MongoDB. You may provide a projection document to define or restrict fields to return or restrict the amount of data which MongoDB sends to applications.

Example:

db.inventory.insertMany( [
{ item: “journal”, status: “A”, size: { h: 14, w: 21}, instock: [ { store: “A”, qty: 5 } ] },
{ item: “notebook”, status: “A”, size: { h: 8.5, w: 11}, instock: [ { store: “C”, qty: 5 } ] },
{ item: “paper”, status: “D”, size: { h: 8.5, w: 11}, instock: [ { store: “A”, qty: 60 } ] },
{ item: “planner”, status: “D”, size: { h: 22.85, w: 30}, instock: [ { store: “A”, qty: 40 } ] },
{ item: “postcard”, status: “A”, size: { h: 10, w: 15.25}, instock: [ { store: “B”, qty: 15 }] }
]); 

Return All Fields in Matching Documents:

If a projection document is not specified, all fields in the matched documents are returned by the db.collection.find() method.

This example shows all the fields in the inventory list of all documents in which the status is “D”:

db.inventory.find( { status: “D” } )

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *