<CharlieDigital/> Programming, Politics, and uhh…pineapples

23Aug/11Off

Filtering By “Reaching” Into Arrays With Mongo

Posted by Charles Chen

One seriously neat capability with Mongo is the ability to filter by "reaching" into an array and filtering on a property of an object in an array.

This is seriously powerful stuff and incredibly easy with Mongo.  There's an example in the official SDK docs, but it's buried pretty deeply in there.

So here's a short summary. Say we are building an address book.

From the Mongo shell, we would enter:

> use addressbook
switched to db addressbook

To create the "addressbook" repository.

Next, we insert two contacts:

> db.contacts.insert({
... Name: "John",
... Addresses: [
...             {Street: "3 Main St.", Town: "Princeton", State: "NJ"},
...             {Street: "198 5th Ave.", Town: "New York", State: "NY"}
...     ]
... })
> db.contacts.insert({
... Name: "Christy",
... Addresses: [
...             {Street: "47 Park Ave.", Town: "New York", State: "NY"}
...     ]
... })

So what if we want to find the list of all contacts who have an address in the state "NY"?

In Mongo, this can be accomplished with the following query:

> db.contacts.find({ "Addresses.State" : "NY" } , { Name: 1 })
{ "_id" : ObjectId("4e54148fdf15eca8a7d2e6d5"), "Name" : "John" }
{ "_id" : ObjectId("4e5414b5df15eca8a7d2e6d6"), "Name" : "Christy" }

Love that simplicity.

Filed under: Mongo No Comments
29Jul/11Off

Working with GUIDs in MongoDB and ASP.NET MVC3

Posted by Charles Chen

Just a small tip for those looking to use GUIDs as document IDs in MongoDB in conjunction with ASP.NET MVC3: it's a lot more straightforward than it may seem at the onset.

These examples are based off of the ASP.NET MVC3 tutorials...except with MongoDB instead of EF+SQL Server.

I've set up my model class like so:

public class Movie
{
    [BsonId]
    public Guid ID { get; set; }
    public string Title { get; set; }
    public DateTime ReleaseDate { get; set; }
    public string Genre { get; set; }
    public decimal Price { get; set; }
}

When the application creates an object and persists it to the database, you'll see that it shows up like this in the Mongo console (I've formatted the JSON for clarity):

> db.movies.find()
{
   "_id":BinData(3,"n2FLBkAkhEOCkX42BGXRqg=="),
   "Title":"Test",
   "ReleaseDate":   ISODate("2011-05-11T04:00:00   Z"),
   "Genre":"Comedy",
   "Price":"9.99"
}

If you try to serialize this to JSON, instead of getting a GUID string, you'll get:

// Get a document
BsonDocument document = movies.FindOneAs<BsonDocument>();

// Direct to JSON
document.ToJson();
/*
{
   "_id":new BinData(3,
   "n2FLBkAkhEOCkX42BGXRqg=="   ),
   "Title":"Test",
   "ReleaseDate":   ISODate("2011-05-11T04:00:00   Z"),
   "Genre":"Comedy",
   "Price":"9.99"
}
*/

// With settings
JsonWriterSettings settings = new JsonWriterSettings{OutputMode = JsonOutputMode.JavaScript };
document.ToJson(settings);
/*
{
   "_id":{
      "$binary":"n2FLBkAkhEOCkX42BGXRqg==",
      "$type":"03"
   },
   "Title":"Test",
   "ReleaseDate":Date(1305086400000),
   "Genre":"Comedy",
   "Price":"9.99"
}
*/

This is somewhat inconvenient if you want to work with it from a pure JavaScript perspective; I was hoping that it would have returned a GUID as a string instead.  I was also concerned that this meant that I'd have to manage this manually as well on the server side in my actions, but it turns out that it works better than expected.  The only caveat is that you have to use "_id" when creating queries; otherwise, you can use the GUID as-is and the Mongo APIs will convert it behind the scenes:

public ActionResult Details(Guid id)
{
    MongoCollection<Movie> movies = _database.GetCollection<Movie>("movies");

    // No need to mess with the GUID; use it as is.
    Movie movie = movies.FindOneAs<Movie>(Query.EQ("_id", id)); 

    return View(movie);
}

You can see the result below in the browser:

Note the properly formatted GUID in the URL

So far, so good with my little Mongo+MVC3 experiment :D

Filed under: .Net, Mongo, MVC No Comments
5Jul/11Off

The Beauty of Mongo

Posted by Charles Chen

MongoDB is sexy.  Sexy as hell.

This isn't the first or the best intro to Mongo+.NET, but I hope this one can show you how it finally solves the object-relational disconnect with an easy to follow example.

Let's start with the model:

#region prologue

// Charles Chen

#endregion

using System;
using System.Collections.Generic;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace MongoAssessmentsTest
{
    public class Assessment
    {
        private DateTime _created;
        private string _description;
        [BsonId]
        private ObjectId _id;
        private List<ContentItem> _items;
        private DateTime _lastUpdated;
        private string _ownerId;
        private List<string> _tags;
        private string _title;

        public ObjectId Id
        {
            get { return _id; }
            set { _id = value; }
        }

        public string OwnerId
        {
            get { return _ownerId; }
            set { _ownerId = value; }
        }

        public string Title
        {
            get { return _title; }
            set { _title = value; }
        }

        public string Description
        {
            get { return _description; }
            set { _description = value; }
        }

        public List<string> Tags
        {
            get { return _tags; }
            set { _tags = value; }
        }

        public DateTime Created
        {
            get { return _created; }
            set { _created = value; }
        }

        public DateTime LastUpdated
        {
            get { return _lastUpdated; }
            set { _lastUpdated = value; }
        }

        public List<ContentItem> Items
        {
            get { return _items; }
            set { _items = value; }
        }
    }

    public class ContentItem
    {
        private List<Question> _questions;
        private string _text;

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public List<Question> Questions
        {
            get { return _questions; }
            set { _questions = value; }
        }
    }

    [BsonKnownTypes(typeof(CheckboxQuestion), typeof(RadioQuestion), typeof(SelectQuestion), typeof(FreeTextQuestion))]
    public class Question
    {
        private int _order;
        private int _points;
        private string _text;

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public int Order
        {
            get { return _order; }
            set { _order = value; }
        }

        public int Points
        {
            get { return _points; }
            set { _points = value; }
        }
    }

    public class CheckboxQuestion : Question
    {
        private List<string> _answers;
        private List<string> _choices;

        public List<string> Choices
        {
            get { return _choices; }
            set { _choices = value; }
        }

        public List<string> Answers
        {
            get { return _answers; }
            set { _answers = value; }
        }
    }

    public class SelectQuestion : Question
    {
        private string _answer;
        private List<string> _choices;

        public List<string> Choices
        {
            get { return _choices; }
            set { _choices = value; }
        }

        public string Answer
        {
            get { return _answer; }
            set { _answer = value; }
        }
    }

    public class RadioQuestion : SelectQuestion { }

    public class FreeTextQuestion : Question
    {
        private List<string> _acceptableAnswers;
        private string _isAnswerCaseSensitive;

        public string IsAnswerCaseSensitive
        {
            get { return _isAnswerCaseSensitive; }
            set { _isAnswerCaseSensitive = value; }
        }

        public List<string> AcceptableAnswers
        {
            get { return _acceptableAnswers; }
            set { _acceptableAnswers = value; }
        }
    }
}

The beauty of it is the absolute simplicity of working with this model.  Aside from a few attributes, there's not much thought that needs to be given to collections and inheritance hierarchies (though there are additional attributes and classes that can be used to control how these are stored if so desired).  I love it because this approach keeps your domain models clean.

How do we interact with this model?

#region prologue

// Charles Chen

#endregion

using System;
using System.Collections.Generic;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;

namespace MongoAssessmentsTest
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Program program = new Program();
            program.Run();
        }

        private void Run()
        {
            MongoServer server = MongoServer.Create(); // Uses default connection options to localhost

            MongoDatabase database = server.GetDatabase("assessments_db");

            if (!database.CollectionExists("assessments"))
            {
                CommandResult result = database.CreateCollection("assessments");

                if (!result.Ok)
                {
                    Console.Out.WriteLine(result.ErrorMessage);
                    return;
                }
            }

            MongoCollection<Assessment> assessments = database.GetCollection<Assessment>("assessments");

            if (assessments.FindOne(Query.EQ("Title", "Sample 1")) == null)
            {
                // Insert an assessment.
                Assessment a = new Assessment
                {
                    Created = DateTime.Now,
                    Description = "A sample assessment",
                    Title = "Sample 1",
                    Items = new List<ContentItem>
                    {
                        new ContentItem
                        {
                            Questions = new List<Question>
                            {
                                new SelectQuestion
                                {
                                    Text = "Who was the first president of the United States?",
                                    Answer = "George Washington",
                                    Choices = new List<string>
                                    {
                                        "George Washington",
                                        "Abraham Lincoln",
                                        "John Adams"
                                    }
                                },
                                new CheckboxQuestion
                                {
                                    Text = "Which of these is NOT a former US President?",
                                    Answers = new List<string>
                                    {
                                        "Benjamin Franklin",
                                        "Hillary Clinton"
                                    },
                                    Choices = new List<string>
                                    {
                                        "Benjamin Franklin",
                                        "Bill Clinton",
                                        "Hillary Clinton",
                                        "Andrew Jackson",
                                        "James Garfield"
                                    }
                                }
                            }
                        }
                    }
                };

                assessments.Insert(a);
            }

            // Get it as BSON - great for writing straight to a web page
            BsonDocument a1 = assessments.FindOneAs<BsonDocument>(Query.EQ("Title", "Sample 1"));

            Console.Out.WriteLine(a1);

            // Get it as an object - great if you want to work with it on the server.
            Assessment a2 = assessments.FindOneAs<Assessment>(Query.EQ("Title", "Sample 1"));

            Console.Out.WriteLine(a2.Title);
        }
    }
}

Brilliantly simple.  Just fire up mongod.exe and you're ready to rock and roll.  Download the example from here: MongoAssessmentsTest.zip

Filed under: .Net, Dev, Mongo No Comments
1Jul/11Off

Thoughts on MongoDB

Posted by Charles Chen

I just recently posted a short write up on Fluent NHibernate vs. Code First in Enterprise Library 4.1.

I'm starting up a new pet project (or rather re-starting it after a 3 year hiatus) so I've been trying to figure out the underlying technologies that I want to build on (as I mentioned, I've been in the SharePoint space for so long now, that I haven't been up-to-date on the latest developments in DB storage).  To be honest, I wasn't really satisfied with the experience of either FNH or EF4.1, especially as I started to work with my model more and more.

What I realized is that the object-relational disconnect is still there.  I'm starting to think that no amount of lipstick will make that pig presentable.   It doesn't make a whole lot of sense and is an impediment to productivity in today's application-layer-oriented environment (if that makes any sense...); I really, really, really want to stop building database applications and focus my time and energy on the application layer.

Enter MongoDB.  I think this (and the similarly JavaScript-friendly, document-oriented, seemingly less performant CouchDB) are what I've been yearning for in the data storage space.  First, I love JavaScript; it's my favorite language (despite whatever flaws it has) for its flexibility and dynamic nature.   So the fact that Mongo's interface language is JavaScript and that it's data storage format is a form of JSON is really appealing to me.  Second, the document-oriented nature of the storage model -- combined with the JSON storage format -- really goes a long way in terms of solving the object-relational disconnect because, well, the relational part is gone.  You have objects and embedded objects and they map perfectly to JSON objects which in turn map fairly easily into .NET CLR objects.

Of course, one neat thing about the nature of working with JSON from UI to the database is that in some cases, the application layer can be relegated to a relatively dumb pass-through (seems like it's still needed as one area of weakness for Mongo is authentication and authorization).  Get your objects in JSON, bind to them in the browser using KO, modify them, and send them back!  It's a beautiful paradigm that could only be better with an out-of-the-box REST interface like CouchDB.

I'm just digging in, but I'm starting to think that I'm never going to look back at relational databases outside of client work and possibly as a supplementary data store for reporting purposes.  It only takes a few moments to get started with Mongo so give it a shot!

Filed under: Dev, Mongo No Comments