The NoSql movement is gaining more and more inertia. A year or two ago I took a look at CouchDb but this time I wanted to try MongoDB. I have been hearing a lot of nice things about MongoDB, mostly about how simple is to query it and how fast is. So I decided that it was time to actually take it for a spin.
A quick search point me to this odetocode post that was a great starting point to have Mongo running in my dev machine using the defaults and the Driver for C#. So, go and read K. Scott Allen’s post if you want to follow along with the code in the rest of this post.
Detour (some background on NoSql and MongoDB):
If you already know about this you can skip all this paragraph.
The so called NoSql movement is based on the notion that using RDBMS may not be the right choice in some cases. They propose using alternative databases, like key-value pair storages and document databases. To perform operations on the data some make use of Map-Reduce, others like MongoDB use a query mechanism that resembles Sql. One of the most attractive features of this db’s is the ability to easily run clusters and do data sharding. Most of them support both features without a lot of fuss, or that I heard.
MongoDB is a document based db, that stores the information as Binary Json.
End of detour.
What’s a document?
A document is not the equivalent to a Row. A document can be seen as a key-value pair collection of objects. It’s actually a complete object Graph, persisted as Json. Documents are organized into Collections.
Now, to be usable in C# we need to have some mapping from the Document object to our object graph. We could use the Document directly but will be like going back to use DataTables, not very pretty.
1: var movieName = movie["Name"].ToString();
2: var actors = JsonConvert.DeserializeObject<Dictionary<int,string>>(movie["Actors"].ToString());
The simple way to go from Json to an object should be to pass the load of a document to a JsonDeserializer like Json.Net and just get the object back, like this:
1: var movie = JsonConvert.DeserializeObject<Movie>(document.ToString());
But while trying to do so I got an error in the id of the document. Looking at the paymoad of the document the Id looks like this:
1: "_id": ObjectId("4eebf0006829c72c1d000000")
The problem is in the ObjectId function. But do not despair, there is an alternative.
From a document to a fully usable object graph.
The alternative is not pretty but works and is easy to implement. Just pass the document to the root of your object graph, an access the underlying collection via the objects properties. Let’s see how this can look like. (Note: Yes there are some hardcoded strings in there that should be removed. I’m also never closing the connection to the Db, when I probably should).
1: using System;
2: using System.Collections.Generic;
3: using Newtonsoft.Json;
4: using MongoDB.Driver;
5:
6: public class Repository
7: {
8: private Database _db;
9:
10: public Database Db()
11: {
12: if(_db != null) return _db;
13: var server = new Mongo();
14: server.Connect();
15: _db = server.getDB("DynamicProgrammer");
16: return _db;
17: }
18:
19: public void Insert(Document document, string collectionName)
20: {
21: var collection = Db().GetCollection(collectionName);
22: collection.Insert(document);
23: }
24:
25: public IEnumerable<TDocument> getListOf<TDocument>(string whereClause, string fromCollection) where TDocument : IMongoEntity
26: {
27: var docs = Db().GetCollection(fromCollection)
28: .Find(whereClause).Documents;
29: return docsToCollection<TDocument>(docs);
30:
31: }
32:
33: private IEnumerable<TDocument> docsToCollection<TDocument>(IEnumerable<Document> documents) where TDocument : IMongoEntity
34: {
35: var list = new List<TDocument>();
36: var settings = new JsonSerializerSettings();
37:
38: foreach (var document in documents)
39: {
40: var docType = Activator.CreateInstance<TDocument>();
41: docType.InternalDocument = document;
42: list.Add(docType);
43: }
44: return list;
45: }
46:
47: }
48:
IMongoEntity is a simple interface to force our entities to expose the Doc property.
1: public interface IMongoEntity
2: {
3: Document InternalDocument { get; set; }
4: }
5:
And our Movie Entity will look like:
1: public class Movie : IMongoEntity
2: {
3: public string Name
4: {
5: get { return InternalDocument["Name"].ToString(); }
6: set { InternalDocument["Name"] = value; }
7: }
8:
9: public int ProductionYear
10: {
11: get { return (int)InternalDocument["Year"]; }
12: set { InternalDocument["Year"] = value; }
13: }
14:
15: public Dictionary<int, string> Actors
16: {
17: get { return JsonConvert.DeserializeObject<Dictionary<int, string>>(InternalDocument["Actors"].ToString()); }
18: set { InternalDocument["Actors"] = value; }
19: }
20:
21: public Document InternalDocument { get; set; }
22: }
Notice I’m using JsonConverter to return a complex type, we should probably encapsulate that functionality in some kind of base class, maybe convert IMongoEntity into an abstract class that provides some basic utilities.
I will try to dig deeper into MongoDB, seems to be really suited to a Domain first approach. I wasn’t able to have anything but the most simple queries working, so I will have to take a closer look at the syntax.