Thursday, January 17, 2013

Focus on CouchDB

Basically I created 3 parallel console solutions: ConsoleCouch1, ConsoleMongo1, and ConsoleRaven1. I was going to compare the 3 piece-by-piece, but I decided that would be too confusing for both of us. So, instead we'll examine the programs as wholes.

I'll tackle CouchDB first...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using LoveSeat;

namespace ConsoleCouch1
{
    class Program
    {
        static void Main(string[] args)
        {
            string DATABASE = "console1";
            string DESIGN_DOC = "names";
            CouchDatabase database;
            CouchClient client = new CouchClient();
        //    client.DeleteDatabase(DATABASE);
            if (!client.HasDatabase(DATABASE))
            {
                client.CreateDatabase(DATABASE);
                database = client.GetDatabase(DATABASE);

                string designDoc =
                    "{\"_id\":\"_design/" + DESIGN_DOC + "\"," +
                    "\"views\": { " +
                                       "\"all\": {" +
                                                      "\"map\": \"function(doc) {emit(null, doc)}\"" +
                                                "}," +
                                       "\"by_name\": {" +
                                                      "\"map\": \"function(doc) {emit([doc.FirstName, doc.LastName], doc)}\"" +
                                                     "}" +
                               "}" +
                   "}";
                database.SaveDocument(new Document(designDoc));
            }
            else
            {
                database = client.GetDatabase(DATABASE);
            } 
            database.SetDefaultDesignDoc(DESIGN_DOC);

            Console.Write("First name: ");
            string first = Console.ReadLine();

            Console.Write("Last name: ");
            string last = Console.ReadLine();

            Name input = new Name() { FirstName = first, LastName = last };

            var options = new ViewOptions();
            options.Key.Add(first);
            options.Key.Add(last);
            int dupCount = database.View<Name>("by_name", options).Items.Count();

            if (dupCount != 0)
            {
                Console.WriteLine("{0} {1} was already on file", first, last);
                Console.WriteLine("dupCount = {0}", dupCount);
            }
            else
            {
                Console.WriteLine("Proceed...");
                database.SaveDocument(new Document<Name>(input));
                Console.WriteLine("{0} {1} was added", first, last);
            }

            Console.WriteLine("Names include:");

            var names = database.View<Name>("all").Items;
            foreach (Name name in names)
            {
                Console.WriteLine("{0} {1} {2}", name._id, name.FirstName, name.LastName);
            }
        }
    }

    [JsonObject]
    class Name
    {
        [JsonProperty]
        public string _id { get; set; }
        [JsonProperty]
        public string _rev { get; set; }
        [JsonProperty]
        public string FirstName { get; set; }
        [JsonProperty]
        public string LastName { get; set; }
     }
}

I chose the LoveSeat API to interface with CouchDB, using the "loveseat" NuGet package. Note that it comes with its own version of Newtonsoft.Json, something to keep in mind if you use ASP.NET MVC. Note that I use _id and _rev strings, both necessary for if you need the full CRUD spectrum. Also note that I used Json attributes since CouchDB stores data as JSON. 

I had to specifically create the database; you'll get an error if you don't. Also, I created a design document, which stores the Javascript CouchDB uses to run queries. The first "map" function simply retrieves all the data documents. The second receives 2 strings (first name and last name), and retrieves all the documents whose data matches those strings. You need to tell CouchDB which design document to use.

To run a parameterized query,  you create a ViewOptions object and feed it the key values. You then run the View, telling it what class the documents are and giving it the ViewOptions object. The Items in the View contain the retrieved documents, which you then can treat as IEnumerable. If the map function uses "null" in the first parameter of its "emit", you don't need to submit any keys; it's like like a SQL statement without a WHERE.

To store a domain object, you first translate it into JSON, hence the Document creation. The SaveDocument method expects a JSON object. 

If you think some of this sounds painful, but you don't want to give up on CouchDB entirely, look into Couchbase, which I might explain in a future blog. 

1 comment:

  1. There's a new project, MyCouch - a Simple async CouchDb client written in C#. It tries to keep the domain language of CouchDb and not hide the fact that it's HTTP based communication. Currently supports JSON, POCOs, CRUD and ViewQueries. Read more and check out the roadmap: https://github.com/danielwertheim/MyCouch/wiki

    Pull requests are always welcomed.

    //Daniel

    ReplyDelete