Easier projections in MongoDB

 
event

I have already written about the talk I gave in the local user group about MongoDB.
I always try to push the limits of what I want to show by adding a couple of more advanced (yet useful) scenarios. The one I want to describe here is document projections using the .NET official driver.

What is a projection anyway?

The term refers to a result of a query that is a subset of the source of information.
In relational databases, it is a subset of the complete list of columns being queried.
For MongoDB, it is a subset of the fields returned for all matched documents.

Projections in LINQ

In LINQ2SQL (and most likely Entity Framework as well, although I do not have the guts to check it) one can get just the needed subset of information just by selecting the fields that one wants to get in the query into an anonymous (or not anonymous) type.
For example, imagine we have a Northwind-like database, like in the example shipped with LINQPad’s “5 minutes induction”, in which we have a Products table:

Projections01

Here are some LINQ2SQL queries and their translation to SQL:

LINQ2SQL SQL
No Projection
Anonymous Projection
Non-Anonymous Projection

As you can see, it is easy enough to create a projection just by specifying the properties to be retrieved, but in the case of non-anonmymous projections, we would need to “repeat” the properties to be retrieved (in the type definition and in the query).

Projections in MongoDB

Let’s imagine that we have a seriously sized document, and that for a given query we only need a small subset of its payload. We are using the official driver and issuing the following LINQ query:

What are we bringing back from the server?

  1. Only the PlaceName property
  2. The whole document
  3. Stop this trick questions thing already

If you guessed 1) or 3) you are out of luck. As stated in the documentation: projections are performed client-side, bringing the whole document from the server and setting the subset of properties.
Definitely not good for our scenario.

If we can live without LINQ queries (and in my opinion we can) we can use .SetFields() alongside the Fields<> builder to create a true projection that brings only the data we need:

The “problem”? The same as with LINQ2SQL projections: we need to repeat ourselves by specifying all the properties of the projected type.

Can we do better?

Sharp readers might have guessed already that yes, we can do better. If not for anything else, because a paragraph with just “No” feels kind of stupid.

The serialization part of the Mongo driver already contains the concept of a BsonClassMap that contains information about how a type gets serialized and deserialized.
If only one could be used to extract the underlying field names and create an instance of IMongoFields with them… Sprinkle it with a bit of generic caching fairy-dust and projection can be DRY-ed

It even works with or without defining a custom class map. It really is a small useful tool to have inside your MongoDB toolset. Enjoy it.