Both the typed and untyped query builders provide a method for each query operator in the MongoDB query language. You can easily explore the available operators using a tool such as MongoVUE to discover them, then using IntelliSense in Visual Studio to check the available method names that are equivalent to each operator. Whenever I have to build a query using the typed query builder and I don't remember the available operators, I use the Object Browser and check the methods available for MongoDB.Driver.Builders.Query<TDocument>
. The documentation provides the equivalent MongoDB operator. For example, the GTE<TMember>
method tests whether the value of the name element is greater than or equal to the provided value and is equivalent to the $gte
operator (see Figure 11).
Figure 11: Checking the documentation for the MongoDB.Driver.Builders.Query<TDocument>.GTE<TMember> method in the Object Browser in Visual Studio.
The following MongoDB document query retrieves the first 50 games that were played and have a release date equal or greater than January 1, 2013. The results are ordered by release_date
in descending order. You can run the query in the MongoDB shell or you can build it with MongoVUE (Figure 12):
db.games.find( { "release_date" : { "$gte" : ISODate("2013-01-01T00:00:00Z") }, "played" : true } ) .limit(50).sort( { "release_date" : -1 } );
Figure 12: The find document query criteria with the specified sort and limit options in MongoVUE.
The following lines use the typed query builder (Query<Game>
) to build the previously shown JavaScript query document in C#. Notice that the SetSortOrder
and SetLimit
methods specify the sort
and limit
options for the MongoDB cursor generated by the FindAs<Game>
method with the typed query playedGamesQuery
as a parameter. The use of a typed builder for specifying a sort order (SortBy<Game>
) allows the code to use the properties defined in the Game
class for the desired sort key with a lambda expression. Then, a foreach
loop writes the values for some properties of each retrieved Game
instance to the console.
var playedGamesQuery = Query.And( Query<Game>.GTE(g => g.ReleaseDate, new DateTime(2013, 01, 01)), Query<Game>.EQ(g => g.Played, true) ); var playedGames = games .FindAs<Game>(playedGamesQuery) .SetSortOrder(SortBy<Game>.Descending(g => g.ReleaseDate)) .SetLimit(50); foreach (var playedGame in playedGames) { //// Do something with each played game Console.WriteLine("Release date: {0}. Name: '{1}'", playedGame.ReleaseDate, playedGame.Name); }
The MongoDB C# driver 1.4 release introduced support for LINQ, which has since been enhanced. LINQ is a good option when you don't want to spend time learning the MongoDB native query operators and syntax. However, you must take into account that not all the LINQ queries have a reasonable translation to an equivalent MongoDB query, and you will definitely have to work with MongoDB native queries when you want to take full advantage of MongoDB features. In fact, you can inject native MongoDB queries into a LINQ query if needed.
The following lines get a reference to the games
collection object and call the AsQueryable
method to build a LINQ query that just retrieves the first Game
instance with the name equal to "Invaders 2013"
or a null
value if none are found. The LINQ query will produce an expression tree that the MongoDB driver will translate to an equivalent MongoDB query at runtime:
var games = db.GetCollection<Game>("games"); var foundGame = games.AsQueryable().FirstOrDefault(g => g.Name == "Invaders 2013");
The previously shown lines are equivalent to the use of the native typed query builder that called the FindOne method:
var games = db.GetCollection<Game>("games"); var gameQuery = Query<Game>.EQ(g => g.Name, "Invaders 2013"); var foundGame = games.FindOne(gameQuery);
It is also possible to build the query that retrieves the first 50 games that were played and have a release date equal or greater than January 1, 2013 with a LINQ query:
var dateTimeFrom = new DateTime(2013, 01, 01); var playedGames = games.AsQueryable() .Where(g => (g.ReleaseDate >= dateTimeFrom) && g.Played) .OrderByDescending(g => g.ReleaseDate) .Take(50);
This imperative LINQ query is equivalent to the following declarative LINQ query, which also translates to a MongoDB query document:
var playedGames = (from game in games.AsQueryable() where (game.ReleaseDate >= dateTimeFrom) && game.Played orderby game.ReleaseDate descending select game).Take(50);
If you don't want to work with your domain classes because the document schemata are completely dynamic, you can work with generic BsonDocument
instances and use strings to specify the field names in the queries. In order to retrieve the field values, you must call the GetElement
method with the desired field name as a parameter (Figure 13). Recall that the BsonDocument
is composed of field-value pairs, and the value might be an embedded document.
var dateTimeFrom = new DateTime(2013, 01, 01); var gamesBson = db.GetCollection("games"); var playedGamesQueryBson = Query.And( Query.GTE("release_date", dateTimeFrom), Query.EQ("played", true) ); var playedGamesBson = gamesBson .Find(playedGamesQueryBson) .SetSortOrder(SortBy.Descending("release_date")) .SetLimit(50); foreach (var playedGameBson in playedGamesBson) { //// Do something with each played game Console.WriteLine("Release date: {0}. Name: '{1}'", playedGameBson.GetElement("release_date").Value, playedGameBson.GetElement("name").Value); }
Figure 13: The find document query criteria with the specified sort and limit options in MongoVUE.
Conclusion
In this article, I've shown how to start working with a MongoDB GUI tool and with the C# driver in a Windows console application. I've explained several options for generating your own domain classes and mapping them to their underlying MongoDB document-oriented schema. I've also provided examples of queries using the different options that the MongoDB C# driver offers. In the next article, I'll explain how to perform other operations with the MongoDB C# driver in a more complex application.
Gaston Hillar is an expert in Windows-based programming who writes frequently for Dr. Dobb's.