# Project

## Include and Exclude

You can use a projection stage into your pipeline to either exclude existing fields or add new ones. To add a projection stage use a `ProjectionDefinitionBuilder<T>` and start excluding or including fields. The end result is a instance of `ProjectionDefinition<T>`. Finally, use it after the `Find` method since the `IFindFluent` interface allows you to **chain** multiple methods calls.

{% tabs %}
{% tab title="Syntax" %}

```csharp
// Create a ProjectDefinition<T>

var definition = Builders<T>.Projection
    .Exclude(doc => doc.<field1>)
    .Include(doc => doc.<field2>)
    ...
    .Include(doc => doc.<fieldN>)
    
// Get results
var simpleProjectionResults = await usersCollection
    .Find(Builders<User>.Filter.Empty)
    .Project(simpleProjection) // projection stage
    .ToListAsync();
```

{% endtab %}
{% endtabs %}

The sample uses a projection stage to re-format `User` documents by excluding the *Id* field and only include the *Gender* and *DateOfBirth*.&#x20;

{% tabs %}
{% tab title="C#" %}
{% code title="Projection.cs" %}

```csharp
var collection = database
    .GetCollection<User>(Constants.UsersCollection);

// exclude id, return only gender and date of birth
var simpleProjection = Builders<User>.Projection
    .Exclude(u => u.Id)
    .Include(u => u.Gender)
    .Include(u => u.DateOfBirth);

var simpleProjectionResults = await usersCollection
    .Find(Builders<User>.Filter.Empty)
    .Project(simpleProjection)
    .ToListAsync();
```

{% endcode %}
{% endtab %}

{% tab title="Bson" %}

```csharp
var bsonCollection = database
            .GetCollection<BsonDocument>(Constants.UsersCollection);

var bsonSimpleProjection = Builders<BsonDocument>.Projection
    .Exclude("_id")
    .Include("gender")
    .Include("dateOfBirth");

var bsonSimpleProjectionResults = await bsonCollection
    .Find(Builders<BsonDocument>.Filter.Empty)
    .Project(bsonSimpleProjection)
    .ToListAsync();
```

{% endtab %}

{% tab title="Shell" %}

```javascript
db.users.aggregate()
    .project({_id: 0, gender: 1, dateOfBirth: 1})

--------------------------- 
        
// sample result
{
	"gender" : 0,
	"dateOfBirth" : ISODate("1972-04-08T20:56:03.950+02:00")
}
```

{% endtab %}

{% tab title="Models" %}

```csharp
public class User
{
    [BsonId]
    [BsonIgnoreIfDefault] // required for replace documents 
    public ObjectId Id { get; set; }
    public Gender Gender { get; set; }
    public string FirstName {get; set; }
    public string LastName {get; set; }
    public string UserName {get; set; }
    public string Avatar {get; set; }
    public string Email {get; set; }
    public DateTime DateOfBirth {get; set; }
    public AddressCard Address {get; set; }
    public string Phone {get; set; }
    
    [BsonIgnoreIfDefault]
    public string Website {get; set; }
    public CompanyCard Company {get; set; }
    public decimal Salary { get; set; }
    public int MonthlyExpenses { get; set; }
    public List<string> FavoriteSports { get; set; }
    public string Profession { get; set; }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
While you don't have to explicitly exclude all the fields you don't want to include in the final result, you do have to do it for the identifier field **`_id`**, otherwise it will be included
{% endhint %}

## Include custom fields

You can use projection to include new custom calculated fields in the final result. The sample uses a projection stage to re-format `User` documents and project the following new fields:

1. ***fullName***: equal to *firstName* and *lastName* concatenated
2. ***fullNameUpper***: equal to fullName in upper case
3. ***gender***: equal to the string value of the Gender enum
4. ***age***: equal to current year minus the *dateOfBirth'*&#x73; field year

{% tabs %}
{% tab title="C#" %}
{% code title="Projection.cs" %}

```csharp
var collection = database
    .GetCollection<User>(Constants.UsersCollection);

var customProjection = Builders<User>.Projection.Expression(u =>
    new
    {
        fullName = $"{u.FirstName} {u.LastName}",
        fullNameUpper = ToUpperCase($"{u.FirstName} {u.LastName}"),
        gender = u.Gender.ToString(),
        age = DateTime.Today.Year - u.DateOfBirth.Year
    });

var results = await collection.Find(Builders<User>.Filter.Empty)
    .Project(customProjection)
    .ToListAsync();

// private method    
private string ToUpperCase(string value)
{
    return value.ToUpper();
}
```

{% endcode %}
{% endtab %}

{% tab title="Result" %}

```javascript
// sample result
{
  "fullName": "Calvin Lindgren",
  "fullNameUpper": "CALVIN LINDGREN",
  "gender": "Male",
  "age": 21
}
```

{% endtab %}

{% tab title="Models" %}

```csharp
public class User
{
    [BsonId]
    [BsonIgnoreIfDefault] // required for replace documents 
    public ObjectId Id { get; set; }
    public Gender Gender { get; set; }
    public string FirstName {get; set; }
    public string LastName {get; set; }
    public string UserName {get; set; }
    public string Avatar {get; set; }
    public string Email {get; set; }
    public DateTime DateOfBirth {get; set; }
    public AddressCard Address {get; set; }
    public string Phone {get; set; }
    
    [BsonIgnoreIfDefault]
    public string Website {get; set; }
    public CompanyCard Company {get; set; }
    public decimal Salary { get; set; }
    public int MonthlyExpenses { get; set; }
    public List<string> FavoriteSports { get; set; }
    public string Profession { get; set; }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
The driver didn't build any complex query to the MongoDB database, instead it created a projection with the fields used in the projection stage and their values used to build the new anonymous result for each document retrieved
{% endhint %}

## Projection with LINQ

If you want the driver to create the exact query that matches your projection you can build it using an **`IMongoQueryable<T>`** reference. There are some **limitations** though: you cannot use any custom C# functions you want as you did using a `ProjectDefinition<T>`, but only those functions that are supported by the driver. You can get an `IMongoQueryable<T>` reference by calling the **`AsQueryable`** method on a `IMongoCollection<T>` reference.

{% tabs %}
{% tab title="Syntax" %}

```csharp
IMongoCollection<T>
    .AsQueryable()
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="C#" %}
{% code title="Projection.cs" %}

```csharp
// get an IMongoQueryable<T> reference
var usersQueryableCollection = personsDatabase
    .GetCollection<User>(Constants.UsersCollection)
    .AsQueryable();

// 'select new' creates a projection stage
var linqSimpleProjection = 
    from u in usersQueryableCollection
    select new
    {
       fullName = u.FirstName + " " + u.LastName,
       gender = u.Gender == Gender.Male ? "Male" : "Female",
       age = DateTime.Now.Year - u.DateOfBirth.Year
    };

var linqSimpleProjectionResults = 
    await linqSimpleProjection.ToListAsync();
```

{% endcode %}
{% endtab %}

{% tab title="Shell" %}

```javascript
// query created by the driver

db.users.aggregate([
{ "$project" : 
    { 
        "lastName" : { "$concat" : ["$firstName", " ", "$lastName"] }, 
        "gender" : { 
            "$cond" : [{ "$eq" : ["$gender", 0] }, "Male", "Female"] 
         }, 
        "age" : { "$subtract" : [2020, { "$year" : "$dateOfBirth" }] }, 
        "_id" : 0 
    } 
}])

---------------------
// sample results

/* 1 */
{
	"lastName" : "Natasha Predovic",
	"gender" : "Female",
	"age" : 46
},

/* 2 */
{
	"lastName" : "Natasha Dooley",
	"gender" : "Female",
	"age" : 45
}

```

{% endtab %}

{% tab title="Models" %}

```csharp
public class User
{
    [BsonId]
    [BsonIgnoreIfDefault] // required for replace documents 
    public ObjectId Id { get; set; }
    public Gender Gender { get; set; }
    public string FirstName {get; set; }
    public string LastName {get; set; }
    public string UserName {get; set; }
    public string Avatar {get; set; }
    public string Email {get; set; }
    public DateTime DateOfBirth {get; set; }
    public AddressCard Address {get; set; }
    public string Phone {get; set; }
    
    [BsonIgnoreIfDefault]
    public string Website {get; set; }
    public CompanyCard Company {get; set; }
    public decimal Salary { get; set; }
    public int MonthlyExpenses { get; set; }
    public List<string> FavoriteSports { get; set; }
    public string Profession { get; set; }
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
This time the driver did built the exact query defined in the projection stage but if you try to use a custom C# function despite the fact it compiles you will get a runtime error.

```csharp
var linqSimpleProjection = 
    from u in usersQueryableCollection
   select new
   {
       fullName = u.FirstName + " " + u.LastName,
       fullNameUpper = // this will cause an exception
           ToUpperCase($"{u.FirstName} {u.LastName}"),
       gender = u.Gender == Gender.Male ? "Male" : "Female",
       age = DateTime.Now.Year - u.DateOfBirth.Year
   };
```

Exception: **`System.NotSupportedException: ToUpperCase of type MongoDb.Csharp.Samples.Aggregation.ProjectionStage is not supported in the expression tree value(MongoDb.Csharp.Samples.Aggregation.ProjectionStage).ToUpperCase(Format("{0} {1}", {document}{firstName}, {document}{lastName})).`**
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chsakell.gitbook.io/mongodb-csharp-docs/aggregation/project-stage.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
