The Couchbase Client Library 1.2-Beta includes a new API for some basic cluster management.  There are methods for creating, removing and listing buckets.  There are similar methods for managing design documents.  These new features are found in the new class CouchbaseCluster, under the Couchbase.Management namespace.  The main benefit of this new API is that it’s now possible to allow your application to create its bucket and set its design documents when it starts up.

One of the overloads for creating new design documents allows you to specify a Stream as the source of the design document.  With this version, it’s easy to create design documents from a set of files.  You can also just specify a string containing the document.

I recently had an idea to create a simple command line utility to use these design doc management methods to automate the creation of basic views.  The result of this idea is up on GitHub at https://github.com/jzablocki/couchbase-model-views.  Using this framework, you’ll be able to automate the creation of views simply by decorating your existing model classes with custom attributes.

Consider a Beer class with a properties for Name, Description, Brewery and ABV.

public class Beer
{
public string Id { get; set; }

public string Name { get; set; }

public string Description { get; set; }

public float ABV { get; set; }

public string Brewery { get; set; }
}

This class maps to “beer” documents in your Couchbase bucket.

{
“name”: “Samuel Adams Summer Ale”,
“abv”: 5.2,
“type”: “beer”,
“brewery_id”: “110f04db06”,
“description”: “Bright and citrusy, brewed with mysterious grains of…
}

To achieve this mapping, you’d likely use Newtonsoft.Json‘s property mapping attributes.

public class Beer
{
public string Id { get; set; }

[JsonProperty(“name”)]
public string Name { get; set; }

[JsonProperty(“description”)]
public string Description { get; set; }

[JsonProperty(“abv”)]
public float ABV { get; set; }

[JsonProperty(“breweryId”)]
public string Brewery { get; set; }
}

Using two new attributes found in the CouchbaseModelViews.Framework project, you could further decorate this class to declare which properties in this class should be indexed by Couchbase Server.  These attributes are CouchbaseDesignDocument and CouchbaseViewKey.

[CouchbaseDesignDoc(“beers”, “beer”)]
public class Beer
{
public string Id { get; set; }

[JsonProperty(“name”)]
[CouchbaseViewKey(“by_abv_and_name”, “name”, 1)]
[CouchbaseViewKey(“by_name”, “name”)]
public string Name { get; set; }

[JsonProperty(“description”)]
public string Description { get; set; }

[JsonProperty(“abv”)]
[CouchbaseViewKey(“by_abv_and_name”, “abv”, 0)]
public float ABV { get; set; }

[JsonProperty(“breweryId”)]
[CouchbaseViewKey(“by_brewery”, “breweryId”)]
public string Brewery { get; set; }
}

The CouchbaseDesignDoc attribute is set on a model class.  It uses a plain-old-CLR-object (POCO) to define a design document.  If you omit the name argument, a design document with the same name (lowercased) as the class will be created.  If you omit the type argument, views will check that documents have a type property with the value of the name (lowercased) of the class.

The CouchbaseViewKey attribute is set on properties in a POCO class that should be indexed.  For example, the Name property in the Beer class above has a CouchbaseViewKey attribute on it with the arguments “by_name” and “name.”  The view that will result from these values is as follows:

function(doc, meta) {
if (doc.type == “beer” && doc.name) {
emit(doc.name, null);
}
}

The “by_name” argument is the name of this view and the “name” argument defines what property is checked for existence and emitted.

It’s also possible to include composite keys by decorating multiple properties with CouchbaseViewKey attributes containing the same value for the viewName parameter.  Composite keys are demonstrated with “by_abv_and_name” on the ABV and Name properties.  Notice also that there is an optional order parameter that allows you to set the order in which properties are emitted.

function(doc, meta) {
if (doc.type == “beer” && doc.abv && doc.name) {
emit([doc.abv, doc.name], null);
}
}

Once you’ve decorated your class with the appropriate attributes, you can use the CouchbaseModelViewsGenerator project to run the models through the view generator.  This is a simple command line project that requires an app.config with a section listing all assemblies containing models that will be used to create views.

<sectionGroup name=“modelViews”>
<section name=“assemblies” type=“System.Configuration.DictionarySectionHandler”/>
>

<modelViews>
<assemblies>
<add key=“DemoModels” value=“CouchbaseModelViews.DemoModels” />
>
>

Additionally, you will need to configure the CouchbaseClusterCouchbaseCluster instances are creating using the same configuration (code or app.config) as the existing CouchbaseClient.  However, there are now an additional two properties you can set to provide the administrator credentials.

<couchbase>
<servers bucket=“default” bucketPassword=“” username=“Administrator” password=“qwerty”>
<add uri=“http://localhost:8091/pools” />
>
>

Once you’ve setup your app.config, make sure you have the listed assemblies in the bin directory where they can be loaded.  The easiest way to achieve this discover-ability is of course to reference them.  However, if you just want to use a compiled version of the console app, then simply copy them into the bin directory.

When executed, the framework will create a “beers” design document that looks like the following:

{
“views”: {
“by_abv_and_name”: {
“map”: “function(doc, meta) { rnt if (doc.type == beer && doc.abv && doc.name) { rntt emit([doc.abv, doc.name], null); rnt } rn }”
},
“by_name”: {
“map”: “function(doc, meta) { rnt if (doc.type == beer && doc.name) { rntt emit(doc.name, null); rnt } rn }”
},
“by_brewery”: {
“map”: “function(doc, meta) { rnt if (doc.type == beer && doc.breweryId) { rntt emit(doc.breweryId, null); rnt } rn }”
}
}
}

The console contains no actual view creation logic.  It just orchestrates calls to the various plumbing components inside the Framework project.  If you prefer to include the view creation inside of Global.asax or some other app start event, you could simply make these calls yourself.  There are four primary components of the framework.

  • The ConfigParser simply reads the list of assemblies from the config section and generates an enumerable list of assemblies.
  • The ViewBuilder class takes an assembly or enumerable list of assemblies and iterates over each of the types.  For each type found with a CouchbaseDesignDoc attribute, views are constructed.  The Build method of the ViewBuilder returns a dictionary, where the keys are design doc names and the values are the actual design docs.
  • The DesignDocManager takes a dictionary with doc name, design doc pairs and uses the CouchbaseCluster to create design documents.
  • There’s also a ViewRunner class, which will run the newly created views.
var assemblies = ConfigParser.GetAssemblies();
var builder = new ViewBuilder();
builder.AddAssemblies(assemblies.ToList());
var designDocs = builder.Build();
var ddManager = new DesignDocManager();
ddManager.Create(designDocs, (s) => Console.WriteLine(“Created {0} design doc”, s));
var runner = new ViewRunner();
runner.Run(designDocs, (k, v, s) => Console.WriteLine(“[{0}::{1}] Key {2}”, k, v, s[“key”]), 5);

There is no coupling between any two of these three components and you can use them as you wish.  If you have your own assembly gathering facility for example, simply pass it to the ViewBuilder.Build to render the JSON needed to create the views.

At this point, the framework is mostly complete and reasonably tested.  The code has a few more loops than I’d like and I might have overused Tuples , but it works.  Feel free to use it in your projects.  Keep in mind, this is a Couchbase Labs project and isn’t officially supported.  Remember too that it’s using the Beta version of the .NET Couchbase Client Library and the API is subject to change.

Author

Posted by John Zablocki, NET. SDK Developer, Couchbase

John Zablocki is a NET. SDK Developer at Couchbase. John is also the organizer of Beantown ALT.NET and a former adjunct at Fairfield University. You can also check out the book on Amazon named "Couchbase Essentials" which explains how to install and configure Couchbase Server.

10 Comments

  1. I dont understand the concept here. How do I save the new view (couchbase design document) into couchbase?

    1. Hi Sam,

      The new views are saved when you execute the code below (from above). Basically, you just need to pass the ViewBuilder an assembly containing classes marked with the new Couchbase attributes. When you call Create on the DesignDocManager, it will create the views.

      var assemblies = ConfigParser.GetAssemblies();

      var builder = new ViewBuilder();

      builder.AddAssemblies(assemblies.ToList());

      var designDocs = builder.Build();

      var ddManager = new DesignDocManager();

      ddManager.Create(designDocs);

      1. what about connecting to couchbase server and saving this design document there? That is what I am confused…Shouldn\’t we be saving this into couchbase server?

        1. Does Ddmanager save the view into couchbase? What I don\’t understand. Is… What relationship does this code have with the db? How and where does the view get saved into the bucket?

          1. Is there anyway for me to pass the connection and client object instances through code instead of using the configuration file while saving the design Document?

          2. I would appreciate if you could help. I tried your code like this :

            objAssemblies.Add(System.Reflection.Assembly.GetAssembly(typeof(myclass))); //myclass has all the properties decorated with attributes

            builder.AddAssemblies(objAssemblies);

            var designDocs = builder.Build();

            var ddManager = new DesignDocManager();

            ddManager.Create(designDocs);

            But it does not work

  2. I want to be able to use the GetView<myclass>. It always gives me a 404. I tried marking all my model classes with CouchbaseDesignDoc and storing them into couchbase but it does not seem to work if I use GetView…please help..

    1. Hi Matt,

      Have you verified that the views are created on the server after running the ViewBuilder/DesignDocManager code snippet above?

      — John

  3. I\’m going to be demoing this project on our Google Hangout this afternoon (follow Couchbase on Google+ for info). I\’ll post a link here after.

  4. Nice, but I\’d also like to see a way to \”fluently\” design the documents similar to FluentNHibernate instead of attributed based design documents.

Leave a reply