With mongo you can enrich you queries with projection definitions, making it easy to limit the data you retrieve from the database and also shape the data according to your projects preferences.
I will show several ways to achieve projections and some limitations regarding these. In our example we will use the base class Person, and the projection models PersonModel and PersonModel2
public class Book { [BsonId] public ObjectId Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } public class PersonModel { public string Id { get; set; } public string FullName { get; set; } } public class PersonModel2 { public ObjectId Id { get; set; } public string FirstName { get; set; } }
1. The basic Find method of the IMongoCollection returns a fluent interface with allows you to chain query and projection.
ProjectionDefinition<Person, PersonModel> projection1 = Builders.Projection.Expression(p => new PersonModel { Id = p.Id.ToString(), FullName = p.FirstName + " " + p.LastName }); List models = collection.Find(_ => true).Project(projection1).ToList();
2. Note that we are not forced to use the builders class, it’s just a convenient way of grouping implementations. We can always use the class that the builder uses.
ProjectionDefinition<Person, PersonModel> projection2 = new FindExpressionProjectionDefinition<Person, PersonModel>(p => new PersonModel { Id = p.Id.ToString(), FullName = p.FirstName + " " + p.LastName }); List models2 = collection.Find(_ => true).Project(projection2).ToList();
3. Using the FindSync or FindAsync methods. These methods do not provide a fluent interface for building projections, instead they encapsulate this information in the find options object
FindOptions<Person, PersonModel> findOptions = new FindOptions<Person, PersonModel> { Projection = Builders.Projection.Expression(p => new PersonModel { Id = p.Id.ToString(), FullName = p.FirstName + " " + p.LastName }) }; FilterDefinition filter = new ExpressionFilterDefinition(_ => true); List models3 = await (await collection.FindAsync(filter, findOptions)).ToListAsync();
4. In the above methods we used an expression definition to concatenate two strings and returned a new model.
We can chose to keep data as it is, only to limit what we bring from the database. For this we can use Include or Exclude. This operations enforces you to also have the Id (as the original id in the Person model) defined in your projection even if you don;t explicitly include it.
FindOptions<Person, PersonModel2> findOptions2 = new FindOptions<Person, PersonModel2> { Projection = Builders.Projection.Include(x => x.FirstName) }; FilterDefinition filter2 = new ExpressionFilterDefinition(_ => true); List models4 = await (await collection.FindAsync(filter2, findOptions2)).ToListAsync();