MongoDB is one of the many free open source NoSQL databases that exist today. I wanted to try out how well the official drivers for C# worked when using Mono.
On the MongoDB web site they have pre built binaries of MongoDB for almost every platform, i grabbed the 64 bit binary for OSX. No installation is required, just unzip the file and run the “mongod” binary from the directory. There is also a binary called “mongo”, this is the mongo interactive shell which is very useful.
The C# drivers are available, both in binary form and as source, from the C# language center. To get started I fired up MonoDevelop 2.8 running on Mono 2.10.8:
Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.
MonoDevelop has matured a lot since I used it the first time. It is still not as good as Visual Studio, but it is getting there. What takes most time to get used to is the totally different set of shortcuts, and the lack of all ReSharper magic.
Let me break down a simple console application example on how to use MongoDB using the official driver from 10gen:
-
-
-
using System;
-
using System.Collections.Generic;
-
using System.Linq;
-
using MongoDB.Bson;
-
using MongoDB.Driver;
-
using MongoDB.Driver.Builders;
-
-
namespace MongoDbTest
-
{
-
public class Article
-
{
-
public MongoDB.Bson.ObjectId id { get; set; }
-
public string title { get; set; }
-
public string status { get; set; }
-
-
public string[] tags { get; set; }
-
}
-
-
class MainClass
-
{
-
public static void Main (string[] args)
-
{
-
// Connect to the database and get a colleciton
-
MongoServer server = MongoServer.Create ("mongodb://localhost");
-
MongoDatabase db = server.GetDatabase ("mongodb-tutorial");
-
-
// Get an article collection
-
MongoCollection<BsonDocument> articles = db.GetCollection<BsonDocument> ("articles");
-
-
// Empty the collection (so all runs of this program will result in the same output)
-
articles.RemoveAll ();
-
-
-
//
-
// Create two articles
-
//
-
articles.Save (new BsonDocument
-
{
-
{"title", "A MongoDB article"},
-
{"status", "published"},
-
{"tags", new BsonArray { "c#", "mongodb" }}
-
});
-
-
articles.Save (new BsonDocument
-
{
-
{"title", "A Mono article"},
-
{"status", "draft"},
-
{"tags", new BsonArray { "c#", "mono" }}
-
});
-
If you type this in the MongoDB shell, you will see that the following articles are stored in the database:
> db.articles.find() { "_id" : ObjectId("4f097f8e74b5b343b97f56a7"), "title" : "A MongoDB article", "status" : "published", "tags" : [ "c#", "mongodb" ] } { "_id" : ObjectId("4f097f8e74b5b343b97f56a8"), "title" : "A Mono article", "status" : "draft", "tags" : [ "c#", "mono" ] }
-
-
// Find and print the first article
-
BsonDocument firstArticle = articles.FindOne ();
-
-
PrintArticle ( // Access the properties using indexing and then casting using the .As*
-
firstArticle["_id"].AsObjectId,
-
firstArticle["title"].AsString,
-
firstArticle["status"].AsString,
-
(firstArticle["tags"].AsBsonArray).Select (x => x.AsString));
-
-
-
//
-
// Typed collections
-
//
-
-
// Using indexing and the .As* operations is not that convenient. A better way
-
// is to use a typed collection. Earlier we defined a Article class, lets use it:
-
MongoCollection<Article> typedArticles = db.GetCollection<Article> ("articles");
-
Article typedArticle = typedArticles.FindOneAs<Article> ();
-
-
PrintArticle (typedArticle.id, typedArticle.title,
-
typedArticle.status, typedArticle.tags);
-
-
// Save a typed article
-
typedArticles.Save (new Article {
-
title = "An article about MonoDevelop",
-
status = "published",
-
tags = new [] {"c#", "mono", "mono-develop"},
-
});
-
-
-
//
-
// Querying
-
//
-
-
// Find article with status draft
-
Article draftArticle = typedArticles
-
.FindOneAs<Article> (Query.EQ ("status", "draft"));
-
-
PrintArticle (draftArticle.id, draftArticle.title,
-
draftArticle.status, draftArticle.tags);
-
-
-
//
-
// Updating
-
//
-
-
// Lets update the draft article and set status published
-
Console.WriteLine ("Nr of published articles before update:"
-
+ typedArticles.Find (Query.EQ ("status", "published")).Count ());
-
-
typedArticles.Update (
-
new QueryDocument { { "_id", draftArticle.id } },
-
new UpdateDocument { { "$set", new BsonDocument ("status", "published") }
-
// $set is atomic
-
});
-
-
Console.WriteLine ("Nr of pubhlished articles after update:"
-
+ typedArticles.Find (Query.EQ ("status", "published")).Count ());
-
-
-
//
-
// MapReduce
-
//
-
-
// This will count the number of times a tag has been used by using MapReduce
-
var map =
-
"function() {" +
-
" this.tags.forEach(function(t) {" + // Iterate all the tags and
-
" emit(t, { count : 1 }); " + // emit the tag name and initial count
-
" })" +
-
"}";
-
-
var reduce =
-
"function(key, emits) {" + // Reduce by tag name and summarize the result
-
" total = 0;" +
-
" for (var i in emits) {" +
-
" total += emits[i].count;" +
-
" }" +
-
" return { count : total };" +
-
"}";
-
-
-
Console.WriteLine ("MapReduce result:");
-
var mr = typedArticles.MapReduce (map, reduce);
-
foreach (var document in mr.GetResults()) {
-
Console.WriteLine (document.ToJson ());
-
}
-
}
-
-
public static void PrintArticle (ObjectId id, string title,
-
string status, IEnumerable<string> tags)
-
{
-
Console.WriteLine ("id: " + id + ", title: " + title);
-
Console.WriteLine ("- tags: " + string.Join (", ", tags));
-
}
-
}
-
}
-
Output from the program:
id: 4f0989fc74b5b3458ae38105, title: A MongoDB article
- tags: c#, mongodb
id: 4f0989fc74b5b3458ae38105, title: A MongoDB article
- tags: c#, mongodb
id: 4f0989fc74b5b3458ae38106, title: A Mono article
- tags: c#, mono
Nr of pubhlished articles before update:2
Nr of pubhlished articles after update:3
MapReduce result:
{ “_id” : “c#”, “value” : { “count” : 3.0 } }
{ “_id” : “mongodb”, “value” : { “count” : 1.0 } }
{ “_id” : “mono”, “value” : { “count” : 2.0 } }
{ “_id” : “mono-develop”, “value” : { “count” : 1.0 } }
Using the C# driver for MongoDB, is as you can see, very straight forward. Usually when I run applications on Mono I make sure to build all libraries using the Mono compiler, to make sure the Microsoft compiler have not used any optimizations that is not supported under Mono (I have run into this issue in the past). However, in this case the .NET binaries on the MongoDB site seems to work just fine under Mono. I also downloaded the MongoDB C# driver source and build them using MonoDevelop without any problems.
One improvement I would like to see is LINQ support in the driver. It would be so much more convenient to be able to use LINQ and strongly typed objects to query the database or create MapReduce-functions.