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.
// 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();
The sample uses a projection stage to re-format User
documents by excluding the Id field and only include the Gender and DateOfBirth.
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();
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:
fullName: equal to firstName and lastName concatenated
fullNameUpper: equal to fullName in upper case
gender: equal to the string value of the Gender enum
age: equal to current year minus the dateOfBirth's field year
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();
}
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.
IMongoCollection<T>
.AsQueryable()
// 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();
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.
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})).
Last updated