NSimpleDB - Use Amazon´s SimpleDB data model in your applications now - Part 3

In my previous postings about Amazon´s SimpleDB data model and API I explained, what Amazon´s online database service - or to be more precise: tuple space - has to offer in general. If this sounds interesting to you, then now welcome to the desktop. Because it´s the desktop on which you can actually experience what it´s like to use such a tuple space. SimpleDB currently (as of Jan 08) is just in limited beta and you have to line up to get one of the limited test accounts.

But you don´t need to wait for Amazon to open up more. I implemented the SimpleDB data model and API in C# for you to integrate in your desktop or web applications. It´s an Open Source project at Google Code called NSimpleDB as short for .NET SimpleDB. I´d say it pretty much offers all features SimpleDB - but as an embeddable database engine instead of an online service.

Installing NSimpleDB

There are two ways for you to use NSimpleDB: Either you download the source code from the subversion repository of the NSimpleDB site. Then you can browse the sources and compile it yourself. But please note, you need to also install a VistaDb database engine. NSimpleDB internally is based on VistaDb and needs its libraries. As you can imagine I did not want do develop a whole persistence engine just to implement SimpleDB´s tuple space. But you can download an eval copy of VistaDb and need not fear to incur any costs right away. Also VistaDb is working on a free community edition, which I will of course use for NSimpleDB once it´s available.

Or, if you don´t want to mess around with the NSimpleDB source code, you can download the small demo application from the download area, which comes with a complete NSimpleDB engine as just one assembly: NSimpleDB.dll.

Using NSimpleDB

If you´re using the precompiled version of NSimpleDB just reference NSimpleDB.dll from your .NET project. If you compiled NSimpleDB yourself, though, reference NSimpleDB.Service.Contract.dll as well as NSimpleDB.Service.VistaDb.dll from the global bin folder of the source code tree. Also be sure to either have VistaDb installed on the same machine or copy VistaDB.NET20.dll into your projects´s output folder.

In any case you then should "open" the following namespaces in your source code:

using NSimpleDB.Service.Contract;

using NSimpleDB.Service.VistaDb;

Opening a NSimpleDB database

To work with NSimpleDB you need to manage a connection to a database file like with any regular RDBMS product. You can do it with the method pair Open()/Close() like this:

VistaDbSimpleDBService ts = new VistaDbSimpleDBService();

ts.Open("hello.ts");

...

ts.Close();

Just be sure to call Close() at the end. Pass in any name you like to give to your database file. NSimpleDB does not require a certain filename extension.

Or you can rely on the compile to generate the code to automatically close the connection. VistaDbSimpleDBSevice implements IDisposable:

using (VistaDbSimpleDBService ts = new VistaDbSimpleDBService("hello.ts"))

{

    ...

}

Working with domains

In order to store any data in your NSimpleDB tuple space you need to create a domain first. It´s as simple as this:

ts.CreateDomain("contacts");

No return value, nothing. This operation is idempotent. You can´t create a domain twice. From then on you can use the domain name in other operations.

To delete a domain, call the opposite operation:

ts.DeleteDomain("contacts");

This operation also is idempotent, don´t worry. And it´s asynchronous. Although the domain will become inaccessible right away for future operations, current operations are not interrupted and the actual deletion will take place at a later time.

Reflecting on domains then can be as easy as this:

string[] domainNames;

domainNames = ts.ListDomains();

foreach (string d in domainNames)

    Console.WriteLine(d);

But this is just the NSimpleDB way of doing it. Amazon´s SimpleDB does not provide such a simple way for retrieving all domain names. Instead the resultset is returned in pages. You determine the page size on the initial call to ListDomains():

domainNames = ts.ListDomains(10, out nextToken);

Also you pass a token variable into which the method puts an identifier for the next page. To retrieve it, call ListDomains() again just with this token:

domainNames = ts.ListDomains(ref nextToken);

If there are no more pages, the token is set to null.

Here´s how you can retrieve all domain names in a pagewise manner using these operations:

string[] domainNames;

string nextToken;

domainNames = ts.ListDomains(3, out nextToken);

while (domainNames.Length > 0)

{

    foreach (string d in domainNames)

        Console.WriteLine(d);

    domainNames = ts.ListDomains(ref nextToken);

}

Working with items and attributes

Storing items

Once you created a domain you can start storing items in it. Each item is uniquely identified by the domain name and its item name. The attributes to put into the item are passed as ISimpleDBAttribute objects. NSimpleDB for this reason provides the SimpleDBAttribute class:

ts.PutAttributes("contacts",

                "123",

                new SimpleDBAttribute("Firstname", "John"),

                new SimpleDBAttribute("Lastname", "Doe"));

You can freely choose an item name. You even need to choose one. It´s the primary key of the item, so to speak.

To later add an attribute to an item, just call PutAttributes() again and pass it the additional attribute:

ts.PutAttributes("contacts",

                "123",

                new SimpleDBAttribute("DOB", "1972-10-23"));

Adding attributes is so easy, because they can contain multiple values. In the relational data model you would need to define more than one column or even set up a second table for this kind of 1:n relationship. Not so with (N)SimpleDB:

ts.PutAttributes("contacts",

                "123",

                new SimpleDBAttribute("Phone", "555-1234"),

                new SimpleDBAttribute("Phone", "0170-332 3483"));

But then, how do you replace an attribute´s value? Pass it by explicitly stating you want it to be replaced:

ts.PutAttributes("contacts",

                "123",

                new SimpleDBAttribute("Firstname", "Peter", true));

Create the attribute object with true for the replace parameter.

Retrieving attributes

Retrieving an item is even easier than storing it. What you get is an array of ISimpleDBAttribute objects:

ISimpleDBAttribute[] attributes;

attributes = ts.GetAttributes("contacts", "123");

 

foreach (ISimpleDBAttribute a in attributes)

    Console.WriteLine("{0}={1}", a.Name, a.Value);

The default is to return all attributes with all their values. But you can limit the number of attributes by passing in explicit attribute names:

attributes = ts.GetAttributes("contacts", "123", "Firstname", "Phone");

 

Deleting attributes and items

You can delete attributes from an item at any time. Just specify their names. They´ll be purged from the item with all their values:

ts.DeleteAttributes("contacts", "123", "Phone");

Once all attributes are gone, the item is gone as well. To make this easier, there is a shortcut. Just don´t specify any attribute name at all:

ts.DeleteAttributes("contacts", "123");

 

Querying for items

Use GetAttributes() to retrieve a single item. But for that you have to know the item´s name. Where do you get this item name from, though? Just use a query:

string[] itemNames;

itemNames = ts.Query("contacts", "['Firstname'='John']");

SimpleDB´s query syntax is just about conditions the items, you´re looking for, have to fulfill. So it´s not a full blown data retrieval/manipulation language like SQL, but more just like a syntax for logical expressions. Here´s a second example to illustrate this:

itemNames = ts.Query("contacts", "['Firstname'='John' OR 'Firstname'='Peter'] UNION ['Lastname'='Davis']");

To load the attributes for the found items, just call GetAttributes() in a second step:

foreach (string itemName in itemNames)

{

    Console.WriteLine(itemName);

    ISimpleDBAttribute[] attributes;

    attributes = ts.GetAttributes("contacts", itemName);

    foreach (ISimpleDBAttribute a in attributes)

        Console.WriteLine("{0}={1}", a.Name, a.Value);

}

That´s it. Easy - but different from working with SQL.

Read more about the query syntax in my previous posting.

Since a query potentially matches a large number of items, SimpleDB´s querying is paged. NSimpleDB sports an unpaged version (see above), but of course also paged querying. It works like paged domain retrieval:

string[] itemNames;

string nextToken;

itemNames = ts.Query("contacts",

                    "...",

                    10,

                    out nextToken);

while (itemNames.Length > 0)

{

    foreach (string itemName in itemNames)

    {

        ...

    }

    itemNames = ts.Query(ref nextToken);

}

Conclusion

This is NSimpleDB. A simple data persistence API - but unlike your regular RDBMS. Nevertheless I find it very interesting an am looking forward for Amazon to open up its beta program for SimpleDB. But until then you can get acquaintant with SimpleDB´s data model by using NSimpleDB locally. Enjoy!

PS: Let me know any questions and suggestions you might have. Just email me via my homepage.

No Comments